1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.fcp.client.http; 15 16 import com.google.errorprone.annotations.concurrent.GuardedBy; 17 import java.io.Closeable; 18 19 /** 20 * A base class for building a Java/JNI-based implementation of the C++ {@code HttpClient} 21 * interface. 22 * 23 * <p>This class is defined in conjunction with the {@code java_http_client.cc/h} C++ code that 24 * invokes it via JNI. 25 * 26 * <p>A note on thread safety: 27 * 28 * <ol> 29 * <li>Incoming calls from the native layer can generally come from any thread, and hence 30 * implementations of these classes must be thread safe. 31 * <li>Outgoing calls to the native layer (e.g. {@link #readRequestBody}, {@link 32 * #onResponseStarted}, etc.) may also be made from any thread, but for a single {@link 33 * HttpRequestHandle} there must never be any concurrent outgoing calls from more than one 34 * thread (hence they are {@code @GuardedBy("this")}). 35 * <li>Outgoing calls to the native layer must only be made once an {@link #performRequests} has 36 * been called on a given {@link HttpRequestHandle}, and not before. 37 * </ol> 38 */ 39 public abstract class HttpClientForNative implements Closeable { 40 /** 41 * A base class for building a Java/JNI-based implementation of the C++ {@code HttpRequestHandle} 42 * and related interfaces. 43 * 44 * <p>This class is defined in conjunction with the {@code java_http_client.cc/h} C++ code that 45 * invokes it via JNI. 46 */ 47 public abstract static class HttpRequestHandle implements Closeable { 48 /** 49 * Called by the native layer to get the request's latest total sent/received bytes stats. May 50 * be called multiple times, and from any thread. 51 * 52 * <p>See C++'s {@code HttpRequestHandle::TotalSentReceivedBytes}. 53 * 54 * @return a serialized {@link JniHttpSentReceivedBytes} proto. 55 */ getTotalSentReceivedBytes()56 public abstract byte[] getTotalSentReceivedBytes(); 57 58 /** 59 * Called by the native layer when the request isn't needed anymore. May be called multiple 60 * times, and from any thread. 61 */ 62 @Override close()63 public abstract void close(); 64 65 /** 66 * Reads up to {@code requestedBytes} of request body data into {@code buffer}, via the native 67 * layer. If the end of the data is reached, then -1 will be placed in the mutable 68 * single-element {@code actualBytesRead} array (this corresponds to C++'s {@code 69 * HttpRequest::ReadBody} returning {@code OUT_OF_RANGE}). Otherwise, at least 1 byte of data 70 * will have been read, and the actual amount of bytes that were read will be placed in the 71 * {@code actualBytesRead} array. 72 * 73 * <p>If the return value is false, then {@link HttpClientForNative} implementation must not 74 * call {@link #onResponseError} anymore, as the native layer will already have called the 75 * corresponding C++ callback. 76 * 77 * <p>See C++'s {@code HttpRequest::ReadBody}. 78 * 79 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 80 * handle. Only one of the callback methods on this handle may be called at any given time (but 81 * they may be called from any thread). 82 * 83 * @return true if the read succeeded (incl. if the end of data was reached), false if the read 84 * failed (in which case the request should be aborted without calling any more callback 85 * methods). 86 */ 87 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 88 @GuardedBy("this") readRequestBody(byte[] buffer, long requestedBytes, int[] actualBytesRead)89 protected boolean readRequestBody(byte[] buffer, long requestedBytes, int[] actualBytesRead) { 90 return HttpClientForNative.readRequestBody( 91 nativeHandle, buffer, requestedBytes, actualBytesRead); 92 } 93 94 /** 95 * Signals to the native layer that the response headers (provided as a serialized {@link 96 * JniHttpResponse}) have been received. 97 * 98 * <p>See C++'s {@code HttpRequestCallback::OnResponseStarted}. 99 * 100 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 101 * handle. Only one of the callback methods on this handle may be called at any given time (but 102 * they may be called from any thread). 103 * 104 * @return true if the response headers were successfully processed, false if not (in which case 105 * the request should be aborted without calling any more callback methods). 106 */ 107 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 108 @GuardedBy("this") onResponseStarted(byte[] responseProto)109 protected boolean onResponseStarted(byte[] responseProto) { 110 return HttpClientForNative.onResponseStarted(nativeHandle, responseProto); 111 } 112 113 /** 114 * Signals to the native layer that an error (provided as a serialized {@link 115 * com.google.rpc.Status} proto) occurred before the response headers were received. 116 * 117 * <p>See C++'s {@code HttpRequestCallback::OnResponseError}. 118 * 119 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 120 * handle. Only one of the callback methods on this handle may be called at any given time (but 121 * they may be called from any thread). 122 */ 123 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 124 @GuardedBy("this") onResponseError(byte[] statusProto)125 protected void onResponseError(byte[] statusProto) { 126 HttpClientForNative.onResponseError(nativeHandle, statusProto); 127 } 128 129 /** 130 * Provides {@code bytesAvailable} bytes of the response body to the native layer, via {@code 131 * data}. 132 * 133 * <p>See C++'s {@code HttpRequestCallback::OnResponseBody}. 134 * 135 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 136 * handle. Only one of the callback methods on this handle may be called at any given time (but 137 * they may be called from any thread). 138 * 139 * @return true if the data was successfully processed, or false if not (in which case the 140 * request should be aborted without calling any more callback methods). 141 */ 142 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 143 @GuardedBy("this") onResponseBody(byte[] data, int bytesAvailable)144 protected boolean onResponseBody(byte[] data, int bytesAvailable) { 145 return HttpClientForNative.onResponseBody(nativeHandle, data, bytesAvailable); 146 } 147 148 /** 149 * Signals to the native layer that an error (provided as a serialized {@link 150 * com.google.rpc.Status} proto) occurred while reading the response body. 151 * 152 * <p>See C++'s {@code HttpRequestCallback::OnResponseBodyError}. 153 * 154 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 155 * handle. Only one of the callback methods on this handle may be called at any given time (but 156 * they may be called from any thread). 157 */ 158 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 159 @GuardedBy("this") onResponseBodyError(byte[] statusProto)160 protected void onResponseBodyError(byte[] statusProto) { 161 HttpClientForNative.onResponseBodyError(nativeHandle, statusProto); 162 } 163 164 /** 165 * Signals to the native layer that the request completed successfully. 166 * 167 * <p>See C++'s {@code HttpRequestCallback::OnResponseBodyCompleted}. 168 * 169 * <p>Must only be called <strong>after</strong> {@link #performRequests} is called on this 170 * handle. Only one of the callback methods on this handle may be called at any given time (but 171 * they may be called from any thread). 172 */ 173 // Note: can be overridden in unit tests, to intercept/mock out calls to the native layer. 174 @GuardedBy("this") onResponseCompleted()175 protected void onResponseCompleted() { 176 HttpClientForNative.onResponseCompleted(nativeHandle); 177 } 178 179 /** 180 * A field that native code uses to associate a native pointer with this object. This field must 181 * never be modified by Java code. 182 */ 183 // Note: this field is volatile to ensure that if it is read from a different thread than the 184 // one that wrote to it earlier, the second thread will see the updated value. 185 private volatile long nativeHandle = 0; 186 } 187 188 /** 189 * Creates an {@link HttpRequestHandle} for use with {@link #performRequests}. 190 * 191 * <p>May be called from any thread. 192 * 193 * @param requestProto a serialized {@link JniHttpRequest} proto. 194 */ enqueueRequest(byte[] requestProto)195 public abstract HttpRequestHandle enqueueRequest(byte[] requestProto); 196 197 /** 198 * Performs the requests corresponding to the given objects, which must be {@link 199 * HttpRequestHandle} instances previously returned by {@link #enqueueRequest}. 200 * 201 * <p>May be called from any thread. 202 * 203 * @return a serialized {@link com.google.rpc.Status} proto indicating success or failure. 204 */ 205 // NOTE: The parameter type is an 'Object[]' array, because this makes it easier for the native 206 // code calling this over JNI to construct the array (it can simply look up the 'Object') class. 207 // The Java implementation is expected to downcast the objects in the array to its RequestHandle 208 // implementation class. performRequests(Object[] requests)209 public abstract byte[] performRequests(Object[] requests); 210 211 /** 212 * Called by native when the client is no longer used and all resources can be released. May be 213 * called multiple times, and from any thread. 214 */ 215 @Override close()216 public abstract void close(); 217 218 // The actual native callback methods, which the HttpRequestHandle class provides wrappers for. 219 // See that class's docs for more info. readRequestBody( long nativeRequestHandle, byte[] buffer, long requestedBytes, int[] actualBytesRead)220 private static native boolean readRequestBody( 221 long nativeRequestHandle, byte[] buffer, long requestedBytes, int[] actualBytesRead); 222 onResponseStarted(long nativeRequestHandle, byte[] responseProto)223 private static native boolean onResponseStarted(long nativeRequestHandle, byte[] responseProto); 224 onResponseError(long nativeRequestHandle, byte[] statusProto)225 private static native void onResponseError(long nativeRequestHandle, byte[] statusProto); 226 onResponseBody( long nativeRequestHandle, byte[] data, int bytesAvailable)227 private static native boolean onResponseBody( 228 long nativeRequestHandle, byte[] data, int bytesAvailable); 229 onResponseBodyError(long nativeRequestHandle, byte[] statusProto)230 private static native void onResponseBodyError(long nativeRequestHandle, byte[] statusProto); 231 onResponseCompleted(long nativeRequestHandle)232 private static native void onResponseCompleted(long nativeRequestHandle); 233 } 234