mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-19 19:42:19 +02:00
libsignal-net: ChatService jni bridge
This commit is contained in:
parent
23764a50e8
commit
d7a4b8c817
7
java/.gitignore
vendored
Normal file
7
java/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# IntelliJ specific files/directories
|
||||||
|
out
|
||||||
|
.shelf
|
||||||
|
.idea
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
*.iml
|
@ -0,0 +1,154 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.signal.libsignal.net;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.signal.libsignal.internal.CalledFromNative;
|
||||||
|
import org.signal.libsignal.internal.CompletableFuture;
|
||||||
|
import org.signal.libsignal.internal.FilterExceptions;
|
||||||
|
import org.signal.libsignal.internal.Native;
|
||||||
|
import org.signal.libsignal.internal.NativeHandleGuard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an API of communication with the Chat Service.
|
||||||
|
*
|
||||||
|
* <p>An instance of this object is obtained via call to {@link Network#createChatService(String,
|
||||||
|
* String)} method.
|
||||||
|
*/
|
||||||
|
public class ChatService extends NativeHandleGuard.SimpleOwner {
|
||||||
|
|
||||||
|
private final TokioAsyncContext tokioAsyncContext;
|
||||||
|
|
||||||
|
ChatService(
|
||||||
|
final TokioAsyncContext tokioAsyncContext,
|
||||||
|
final Network.ConnectionManager connectionManager,
|
||||||
|
final String username,
|
||||||
|
final String password) {
|
||||||
|
super(
|
||||||
|
connectionManager.guardedMap(
|
||||||
|
connectionManagerHandle ->
|
||||||
|
Native.ChatService_new(connectionManagerHandle, username, password)));
|
||||||
|
this.tokioAsyncContext = tokioAsyncContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates termination of the underlying connection to the Chat Service. After the service is
|
||||||
|
* disconnected, it will not attempt to automatically reconnect until one of the request methods
|
||||||
|
* is used (e.g. {@link #unauthenticatedSend(Request)}).
|
||||||
|
*
|
||||||
|
* <p>Note: the same instance of {@code ChatService} can be reused after {@code disconnect()} was
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @return a future that completes when the underlying connection is terminated.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public CompletableFuture<Void> disconnect() {
|
||||||
|
return tokioAsyncContext.guardedMap(
|
||||||
|
asyncContextHandle ->
|
||||||
|
guardedMap(
|
||||||
|
chatServiceHandle ->
|
||||||
|
Native.ChatService_disconnect(asyncContextHandle, chatServiceHandle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends request to the Chat Service over an unauthenticated channel.
|
||||||
|
*
|
||||||
|
* @param req request object
|
||||||
|
* @return a {@code CompletableFuture} of a {@link Response}
|
||||||
|
* @throws MalformedURLException is thrown if {@code pathAndQuery} component of the request has an
|
||||||
|
* invalid structure.
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Response> unauthenticatedSend(final Request req)
|
||||||
|
throws MalformedURLException {
|
||||||
|
final InternalRequest internalRequest = buildInternalRequest(req);
|
||||||
|
try (final NativeHandleGuard asyncContextHandle = new NativeHandleGuard(tokioAsyncContext);
|
||||||
|
final NativeHandleGuard chatServiceHandle = new NativeHandleGuard(this);
|
||||||
|
final NativeHandleGuard requestHandle = new NativeHandleGuard(internalRequest)) {
|
||||||
|
return Native.ChatService_unauth_send(
|
||||||
|
asyncContextHandle.nativeHandle(),
|
||||||
|
chatServiceHandle.nativeHandle(),
|
||||||
|
requestHandle.nativeHandle(),
|
||||||
|
req.timeoutMillis)
|
||||||
|
.thenApply(o -> (Response) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends request to the Chat Service over an unauthenticated channel.
|
||||||
|
*
|
||||||
|
* <p>In addition to the response, an object containing debug information about the request flow
|
||||||
|
* is returned.
|
||||||
|
*
|
||||||
|
* @param req request object
|
||||||
|
* @return a {@code CompletableFuture} of a {@link ResponseAndDebugInfo}
|
||||||
|
* @throws MalformedURLException is thrown if {@code pathAndQuery} component of the request has an
|
||||||
|
* invalid structure.
|
||||||
|
*/
|
||||||
|
public CompletableFuture<ResponseAndDebugInfo> unauthenticatedSendAndDebug(final Request req)
|
||||||
|
throws MalformedURLException {
|
||||||
|
final InternalRequest internalRequest = buildInternalRequest(req);
|
||||||
|
try (final NativeHandleGuard asyncContextHandle = new NativeHandleGuard(tokioAsyncContext);
|
||||||
|
final NativeHandleGuard chatServiceHandle = new NativeHandleGuard(this);
|
||||||
|
final NativeHandleGuard requestHandle = new NativeHandleGuard(internalRequest)) {
|
||||||
|
return Native.ChatService_unauth_send_and_debug(
|
||||||
|
asyncContextHandle.nativeHandle(),
|
||||||
|
chatServiceHandle.nativeHandle(),
|
||||||
|
requestHandle.nativeHandle(),
|
||||||
|
req.timeoutMillis)
|
||||||
|
.thenApply(o -> (ResponseAndDebugInfo) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static InternalRequest buildInternalRequest(final Request req) throws MalformedURLException {
|
||||||
|
final InternalRequest result =
|
||||||
|
new InternalRequest(req.method(), req.pathAndQuery(), req.body());
|
||||||
|
req.headers().forEach(result::addHeader);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void release(final long nativeHandle) {
|
||||||
|
Native.Chat_Destroy(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InternalRequest extends NativeHandleGuard.SimpleOwner {
|
||||||
|
InternalRequest(final String method, final String pathAndQuery, final byte[] body)
|
||||||
|
throws MalformedURLException {
|
||||||
|
super(
|
||||||
|
FilterExceptions.filterExceptions(
|
||||||
|
MalformedURLException.class,
|
||||||
|
() -> Native.HttpRequest_new(method, pathAndQuery, body)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void release(final long nativeHandle) {
|
||||||
|
Native.HttpRequest_Destroy(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHeader(final String name, final String value) {
|
||||||
|
guardedRun(h -> Native.HttpRequest_add_header(h, name, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Request(
|
||||||
|
String method,
|
||||||
|
String pathAndQuery,
|
||||||
|
Map<String, String> headers,
|
||||||
|
byte[] body,
|
||||||
|
int timeoutMillis) {}
|
||||||
|
|
||||||
|
public record Response(int status, String message, Map<String, String> headers, byte[] body) {}
|
||||||
|
|
||||||
|
public record DebugInfo(boolean connectionReused, int reconnectCount, IpType ipType) {
|
||||||
|
@CalledFromNative
|
||||||
|
DebugInfo(boolean connectionReused, int reconnectCount, byte ipTypeCode) {
|
||||||
|
this(connectionReused, reconnectCount, IpType.values()[ipTypeCode]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ResponseAndDebugInfo(Response response, DebugInfo debugInfo) {}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.signal.libsignal.net;
|
||||||
|
|
||||||
|
/** Error thrown by Chat Service API. */
|
||||||
|
public class ChatServiceException extends Exception {
|
||||||
|
public ChatServiceException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.signal.libsignal.net;
|
||||||
|
|
||||||
|
/** The order of values in this enum should match {@code IpType} enum in Rust (libsignal-net). */
|
||||||
|
public enum IpType {
|
||||||
|
UNKNOWN,
|
||||||
|
IPv4,
|
||||||
|
IPv6
|
||||||
|
}
|
@ -20,11 +20,15 @@ public class Network {
|
|||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
private Environment(int value) {
|
Environment(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final TokioAsyncContext tokioAsyncContext;
|
||||||
|
|
||||||
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group of the APIs responsible for communication with the SVR3 service.
|
* Group of the APIs responsible for communication with the SVR3 service.
|
||||||
*
|
*
|
||||||
@ -65,25 +69,18 @@ public class Network {
|
|||||||
return this.connectionManager;
|
return this.connectionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectionManager implements NativeHandleGuard.Owner {
|
public ChatService createChatService(final String username, final String password) {
|
||||||
private long nativeHandle;
|
return new ChatService(tokioAsyncContext, connectionManager, username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ConnectionManager extends NativeHandleGuard.SimpleOwner {
|
||||||
private ConnectionManager(Environment env) {
|
private ConnectionManager(Environment env) {
|
||||||
this.nativeHandle = Native.ConnectionManager_new(env.value);
|
super(Native.ConnectionManager_new(env.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long unsafeNativeHandleWithoutGuard() {
|
protected void release(final long nativeHandle) {
|
||||||
return this.nativeHandle;
|
Native.ConnectionManager_Destroy(nativeHandle);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void finalize() {
|
|
||||||
Native.ConnectionManager_Destroy(this.nativeHandle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TokioAsyncContext tokioAsyncContext;
|
|
||||||
private ConnectionManager connectionManager;
|
|
||||||
}
|
}
|
||||||
|
@ -8,21 +8,13 @@ package org.signal.libsignal.net;
|
|||||||
import org.signal.libsignal.internal.Native;
|
import org.signal.libsignal.internal.Native;
|
||||||
import org.signal.libsignal.internal.NativeHandleGuard;
|
import org.signal.libsignal.internal.NativeHandleGuard;
|
||||||
|
|
||||||
class TokioAsyncContext implements NativeHandleGuard.Owner {
|
class TokioAsyncContext extends NativeHandleGuard.SimpleOwner {
|
||||||
private long nativeHandle;
|
|
||||||
|
|
||||||
TokioAsyncContext() {
|
TokioAsyncContext() {
|
||||||
this.nativeHandle = Native.TokioAsyncContext_new();
|
super(Native.TokioAsyncContext_new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long unsafeNativeHandleWithoutGuard() {
|
protected void release(final long nativeHandle) {
|
||||||
return this.nativeHandle;
|
Native.TokioAsyncContext_Destroy(nativeHandle);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void finalize() {
|
|
||||||
Native.TokioAsyncContext_Destroy(this.nativeHandle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.signal.libsignal.net;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.signal.libsignal.internal.Native;
|
||||||
|
|
||||||
|
public class ChatServiceTest {
|
||||||
|
|
||||||
|
private static final int EXPECTED_STATUS = 200;
|
||||||
|
|
||||||
|
private static final String EXPECTED_MESSAGE = "OK";
|
||||||
|
|
||||||
|
private static final byte[] EXPECTED_CONTENT = "content".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
private static final Map<String, String> EXPECTED_HEADERS =
|
||||||
|
Map.of(
|
||||||
|
"user-agent", "test",
|
||||||
|
"forwarded", "1.1.1.1");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertResponse() throws Exception {
|
||||||
|
// empty body
|
||||||
|
final ChatService.Response response1 =
|
||||||
|
(ChatService.Response) Native.TESTING_ChatServiceResponseConvert(false);
|
||||||
|
assertEquals(EXPECTED_STATUS, response1.status());
|
||||||
|
assertEquals(EXPECTED_MESSAGE, response1.message());
|
||||||
|
assertArrayEquals(new byte[0], response1.body());
|
||||||
|
assertEquals(EXPECTED_HEADERS, response1.headers());
|
||||||
|
|
||||||
|
final ChatService.Response response2 =
|
||||||
|
(ChatService.Response) Native.TESTING_ChatServiceResponseConvert(true);
|
||||||
|
assertEquals(EXPECTED_STATUS, response2.status());
|
||||||
|
assertEquals(EXPECTED_MESSAGE, response2.message());
|
||||||
|
assertArrayEquals(EXPECTED_CONTENT, response2.body());
|
||||||
|
assertEquals(EXPECTED_HEADERS, response2.headers());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertDebugInfo() throws Exception {
|
||||||
|
final ChatService.DebugInfo debugInfo =
|
||||||
|
(ChatService.DebugInfo) Native.TESTING_ChatServiceDebugInfoConvert();
|
||||||
|
assertTrue(debugInfo.connectionReused());
|
||||||
|
assertEquals(2, debugInfo.reconnectCount());
|
||||||
|
assertEquals(IpType.IPv4, debugInfo.ipType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertResponseAndDebugInfo() throws Exception {
|
||||||
|
final ChatService.ResponseAndDebugInfo responseAndDebugInfo =
|
||||||
|
(ChatService.ResponseAndDebugInfo) Native.TESTING_ChatServiceResponseAndDebugInfoConvert();
|
||||||
|
|
||||||
|
final ChatService.Response response = responseAndDebugInfo.response();
|
||||||
|
assertEquals(EXPECTED_STATUS, response.status());
|
||||||
|
assertEquals(EXPECTED_MESSAGE, response.message());
|
||||||
|
assertArrayEquals(EXPECTED_CONTENT, response.body());
|
||||||
|
assertEquals(EXPECTED_HEADERS, response.headers());
|
||||||
|
|
||||||
|
final ChatService.DebugInfo debugInfo = responseAndDebugInfo.debugInfo();
|
||||||
|
assertTrue(debugInfo.connectionReused());
|
||||||
|
assertEquals(2, debugInfo.reconnectCount());
|
||||||
|
assertEquals(IpType.IPv4, debugInfo.ipType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ChatServiceException.class)
|
||||||
|
public void testConvertError() throws Exception {
|
||||||
|
Native.TESTING_ChatServiceErrorConvert();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructRequest() throws Exception {
|
||||||
|
final String expectedMethod = "GET";
|
||||||
|
final String expectedPathAndQuery = "/test";
|
||||||
|
final ChatService.Request request =
|
||||||
|
new ChatService.Request(
|
||||||
|
expectedMethod, expectedPathAndQuery, EXPECTED_HEADERS, EXPECTED_CONTENT, 5000);
|
||||||
|
final ChatService.InternalRequest internal = ChatService.buildInternalRequest(request);
|
||||||
|
assertEquals(expectedMethod, internal.guardedMap(Native::TESTING_ChatRequestGetMethod));
|
||||||
|
assertEquals(expectedPathAndQuery, internal.guardedMap(Native::TESTING_ChatRequestGetPath));
|
||||||
|
assertArrayEquals(EXPECTED_CONTENT, internal.guardedMap(Native::TESTING_ChatRequestGetBody));
|
||||||
|
EXPECTED_HEADERS.forEach(
|
||||||
|
(name, value) ->
|
||||||
|
assertEquals(
|
||||||
|
value,
|
||||||
|
internal.guardedMap(h -> Native.TESTING_ChatRequestGetHeaderValue(h, name))));
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ pluginManagement {
|
|||||||
|
|
||||||
rootProject.name = 'libsignal'
|
rootProject.name = 'libsignal'
|
||||||
|
|
||||||
include ':client', ':server', ':shared'
|
include 'client', 'server', 'shared'
|
||||||
|
|
||||||
if (hasProperty('skipAndroid')) {
|
if (hasProperty('skipAndroid')) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -24,25 +24,47 @@ import org.signal.libsignal.protocol.logging.Log;
|
|||||||
*/
|
*/
|
||||||
public class FilterExceptions {
|
public class FilterExceptions {
|
||||||
/** A "functional interface" for an operation that returns an object and can throw. */
|
/** A "functional interface" for an operation that returns an object and can throw. */
|
||||||
public static interface ThrowingNativeOperation<R> {
|
@FunctionalInterface
|
||||||
|
public interface ThrowingNativeOperation<R> {
|
||||||
R run() throws Exception;
|
R run() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A "functional interface" for an operation that returns an {@code int} and can throw. */
|
/** A "functional interface" for an operation that returns an {@code int} and can throw. */
|
||||||
public static interface ThrowingNativeIntOperation {
|
@FunctionalInterface
|
||||||
|
public interface ThrowingNativeIntOperation {
|
||||||
int run() throws Exception;
|
int run() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A "functional interface" for an operation that returns a {@code long} and can throw. */
|
/** A "functional interface" for an operation that returns a {@code long} and can throw. */
|
||||||
public static interface ThrowingNativeLongOperation {
|
@FunctionalInterface
|
||||||
|
public interface ThrowingNativeLongOperation {
|
||||||
long run() throws Exception;
|
long run() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A "functional interface" for an operation that has no result but can throw. */
|
/** A "functional interface" for an operation that has no result but can throw. */
|
||||||
public static interface ThrowingNativeVoidOperation {
|
@FunctionalInterface
|
||||||
|
public interface ThrowingNativeVoidOperation {
|
||||||
void run() throws Exception;
|
void run() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "functional interface" for an operation that maps a {@code long} value to an object and can
|
||||||
|
* throw.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThrowingLongFunction<R> {
|
||||||
|
R apply(long value) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "functional interface" for an operation that accepts a {@code long} value, has no result, and
|
||||||
|
* can throw.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThrowingLongConsumer {
|
||||||
|
void accept(long value) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
private static AssertionError reportUnexpectedException(Exception e) {
|
private static AssertionError reportUnexpectedException(Exception e) {
|
||||||
Log.e("libsignal", "Unexpected checked exception " + e.getClass(), e);
|
Log.e("libsignal", "Unexpected checked exception " + e.getClass(), e);
|
||||||
return new AssertionError(e);
|
return new AssertionError(e);
|
||||||
|
@ -161,6 +161,11 @@ public final class Native {
|
|||||||
public static native CompletableFuture<Long> CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request, int timeoutMillis);
|
public static native CompletableFuture<Long> CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request, int timeoutMillis);
|
||||||
public static native byte[] CdsiLookup_token(long lookup);
|
public static native byte[] CdsiLookup_token(long lookup);
|
||||||
|
|
||||||
|
public static native CompletableFuture ChatService_disconnect(long asyncRuntime, long chat);
|
||||||
|
public static native long ChatService_new(long connectionManager, String username, String password);
|
||||||
|
public static native CompletableFuture<Object> ChatService_unauth_send(long asyncRuntime, long chat, long httpRequest, int timeoutMillis);
|
||||||
|
public static native CompletableFuture<Object> ChatService_unauth_send_and_debug(long asyncRuntime, long chat, long httpRequest, int timeoutMillis);
|
||||||
|
|
||||||
public static native void Chat_Destroy(long handle);
|
public static native void Chat_Destroy(long handle);
|
||||||
|
|
||||||
public static native void ConnectionManager_Destroy(long handle);
|
public static native void ConnectionManager_Destroy(long handle);
|
||||||
@ -291,6 +296,8 @@ public final class Native {
|
|||||||
public static native long HsmEnclaveClient_New(byte[] trustedPublicKey, byte[] trustedCodeHashes) throws Exception;
|
public static native long HsmEnclaveClient_New(byte[] trustedPublicKey, byte[] trustedCodeHashes) throws Exception;
|
||||||
|
|
||||||
public static native void HttpRequest_Destroy(long handle);
|
public static native void HttpRequest_Destroy(long handle);
|
||||||
|
public static native void HttpRequest_add_header(long request, String name, String value);
|
||||||
|
public static native long HttpRequest_new(String method, String path, byte[] bodyAsSlice) throws Exception;
|
||||||
|
|
||||||
public static native long[] IdentityKeyPair_Deserialize(byte[] data);
|
public static native long[] IdentityKeyPair_Deserialize(byte[] data);
|
||||||
public static native byte[] IdentityKeyPair_Serialize(long publicKey, long privateKey);
|
public static native byte[] IdentityKeyPair_Serialize(long publicKey, long privateKey);
|
||||||
@ -595,6 +602,14 @@ public final class Native {
|
|||||||
|
|
||||||
public static native void TESTING_CdsiLookupErrorConvert() throws Exception;
|
public static native void TESTING_CdsiLookupErrorConvert() throws Exception;
|
||||||
public static native CompletableFuture<Object> TESTING_CdsiLookupResponseConvert(long asyncRuntime);
|
public static native CompletableFuture<Object> TESTING_CdsiLookupResponseConvert(long asyncRuntime);
|
||||||
|
public static native byte[] TESTING_ChatRequestGetBody(long request);
|
||||||
|
public static native String TESTING_ChatRequestGetHeaderValue(long request, String headerName);
|
||||||
|
public static native String TESTING_ChatRequestGetMethod(long request);
|
||||||
|
public static native String TESTING_ChatRequestGetPath(long request);
|
||||||
|
public static native Object TESTING_ChatServiceDebugInfoConvert() throws Exception;
|
||||||
|
public static native void TESTING_ChatServiceErrorConvert() throws Exception;
|
||||||
|
public static native Object TESTING_ChatServiceResponseAndDebugInfoConvert() throws Exception;
|
||||||
|
public static native Object TESTING_ChatServiceResponseConvert(boolean bodyPresent) throws Exception;
|
||||||
public static native void TESTING_ErrorOnBorrowAsync(Object input);
|
public static native void TESTING_ErrorOnBorrowAsync(Object input);
|
||||||
public static native CompletableFuture TESTING_ErrorOnBorrowIo(long asyncRuntime, Object input);
|
public static native CompletableFuture TESTING_ErrorOnBorrowIo(long asyncRuntime, Object input);
|
||||||
public static native void TESTING_ErrorOnBorrowSync(Object input);
|
public static native void TESTING_ErrorOnBorrowSync(Object input);
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
package org.signal.libsignal.internal;
|
package org.signal.libsignal.internal;
|
||||||
|
|
||||||
|
import java.util.function.LongConsumer;
|
||||||
|
import java.util.function.LongFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to a Rust object handle while keeping the Java wrapper alive.
|
* Provides access to a Rust object handle while keeping the Java wrapper alive.
|
||||||
*
|
*
|
||||||
@ -21,10 +24,58 @@ public class NativeHandleGuard implements AutoCloseable {
|
|||||||
/**
|
/**
|
||||||
* @see NativeHandleGuard
|
* @see NativeHandleGuard
|
||||||
*/
|
*/
|
||||||
public static interface Owner {
|
public interface Owner {
|
||||||
long unsafeNativeHandleWithoutGuard();
|
long unsafeNativeHandleWithoutGuard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract static class SimpleOwner implements Owner {
|
||||||
|
|
||||||
|
private final long nativeHandle;
|
||||||
|
|
||||||
|
protected SimpleOwner(final long nativeHandle) {
|
||||||
|
this.nativeHandle = nativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void release(long nativeHandle);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long unsafeNativeHandleWithoutGuard() {
|
||||||
|
return nativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected void finalize() {
|
||||||
|
release(this.nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T guardedMap(final LongFunction<T> function) {
|
||||||
|
try (final NativeHandleGuard guard = new NativeHandleGuard(this)) {
|
||||||
|
return function.apply(guard.nativeHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T guardedMapChecked(final FilterExceptions.ThrowingLongFunction<T> function)
|
||||||
|
throws Exception {
|
||||||
|
try (final NativeHandleGuard guard = new NativeHandleGuard(this)) {
|
||||||
|
return function.apply(guard.nativeHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void guardedRun(final LongConsumer consumer) {
|
||||||
|
try (final NativeHandleGuard guard = new NativeHandleGuard(this)) {
|
||||||
|
consumer.accept(guard.nativeHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void guardedRunChecked(final FilterExceptions.ThrowingLongConsumer consumer)
|
||||||
|
throws Exception {
|
||||||
|
try (final NativeHandleGuard guard = new NativeHandleGuard(this)) {
|
||||||
|
consumer.accept(guard.nativeHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Owner owner;
|
private final Owner owner;
|
||||||
|
|
||||||
public NativeHandleGuard(Owner owner) {
|
public NativeHandleGuard(Owner owner) {
|
||||||
|
1
node/Native.d.ts
vendored
1
node/Native.d.ts
vendored
@ -467,6 +467,7 @@ export function TESTING_ChatRequestGetMethod(request: Wrapper<HttpRequest>): str
|
|||||||
export function TESTING_ChatRequestGetPath(request: Wrapper<HttpRequest>): string;
|
export function TESTING_ChatRequestGetPath(request: Wrapper<HttpRequest>): string;
|
||||||
export function TESTING_ChatServiceDebugInfoConvert(): DebugInfo;
|
export function TESTING_ChatServiceDebugInfoConvert(): DebugInfo;
|
||||||
export function TESTING_ChatServiceErrorConvert(): void;
|
export function TESTING_ChatServiceErrorConvert(): void;
|
||||||
|
export function TESTING_ChatServiceResponseAndDebugInfoConvert(): ResponseAndDebugInfo;
|
||||||
export function TESTING_ChatServiceResponseConvert(bodyPresent: boolean): Response;
|
export function TESTING_ChatServiceResponseConvert(bodyPresent: boolean): Response;
|
||||||
export function TESTING_ErrorOnBorrowAsync(_input: null): Promise<void>;
|
export function TESTING_ErrorOnBorrowAsync(_input: null): Promise<void>;
|
||||||
export function TESTING_ErrorOnBorrowIo(asyncRuntime: Wrapper<NonSuspendingBackgroundThreadRuntime>, _input: null): Promise<void>;
|
export function TESTING_ErrorOnBorrowIo(asyncRuntime: Wrapper<NonSuspendingBackgroundThreadRuntime>, _input: null): Promise<void>;
|
||||||
|
@ -33,13 +33,13 @@ describe('chat service api', () => {
|
|||||||
];
|
];
|
||||||
const expectedWithContent: Response = {
|
const expectedWithContent: Response = {
|
||||||
status: status,
|
status: status,
|
||||||
message: undefined,
|
message: 'OK',
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: Buffer.from('content'),
|
body: Buffer.from('content'),
|
||||||
};
|
};
|
||||||
const expectedWithoutContent: Response = {
|
const expectedWithoutContent: Response = {
|
||||||
status: status,
|
status: status,
|
||||||
message: undefined,
|
message: 'OK',
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: undefined,
|
body: undefined,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,7 @@ use std::ops::Deref;
|
|||||||
|
|
||||||
use crate::io::{InputStream, SyncInputStream};
|
use crate::io::{InputStream, SyncInputStream};
|
||||||
use crate::message_backup::MessageBackupValidationOutcome;
|
use crate::message_backup::MessageBackupValidationOutcome;
|
||||||
|
use crate::net::ResponseAndDebugInfo;
|
||||||
use crate::support::{Array, AsType, FixedLengthBincodeSerializable, Serialized};
|
use crate::support::{Array, AsType, FixedLengthBincodeSerializable, Serialized};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1021,6 +1022,125 @@ impl<'a> ResultTypeInfo<'a> for libsignal_net::cdsi::LookupResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ResultTypeInfo<'a> for libsignal_net::chat::Response {
|
||||||
|
type ResultType = JObject<'a>;
|
||||||
|
|
||||||
|
fn convert_into(self, env: &mut JNIEnv<'a>) -> Result<Self::ResultType, BridgeLayerError> {
|
||||||
|
let Self {
|
||||||
|
status,
|
||||||
|
message,
|
||||||
|
body,
|
||||||
|
headers,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// body
|
||||||
|
let body = body.as_deref().unwrap_or(&[]);
|
||||||
|
let body_arr = env.byte_array_from_slice(body)?;
|
||||||
|
|
||||||
|
// message
|
||||||
|
let message_local = env.new_string(message.as_deref().unwrap_or(""))?;
|
||||||
|
|
||||||
|
// headers
|
||||||
|
let headers_map = new_object(
|
||||||
|
env,
|
||||||
|
jni_class_name!(java.util.HashMap),
|
||||||
|
jni_args!(() -> void),
|
||||||
|
)?;
|
||||||
|
let headers_jmap = JMap::from_env(env, &headers_map)?;
|
||||||
|
for (name, value) in headers.iter() {
|
||||||
|
let name_str = env.new_string(name.as_str())?;
|
||||||
|
let value_str = env.new_string(value.to_str().expect("valid header value"))?;
|
||||||
|
headers_jmap.put(env, &name_str, &value_str)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let class = {
|
||||||
|
const RESPONSE_CLASS: &str =
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::Response);
|
||||||
|
get_preloaded_class(env, RESPONSE_CLASS)
|
||||||
|
.transpose()
|
||||||
|
.unwrap_or_else(|| env.find_class(RESPONSE_CLASS))?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(new_object(
|
||||||
|
env,
|
||||||
|
class,
|
||||||
|
jni_args!((
|
||||||
|
status.as_u16().into() => int,
|
||||||
|
message_local => java.lang.String,
|
||||||
|
headers_jmap => java.util.Map,
|
||||||
|
body_arr => [byte]
|
||||||
|
) -> void),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ResultTypeInfo<'a> for libsignal_net::chat::DebugInfo {
|
||||||
|
type ResultType = JObject<'a>;
|
||||||
|
|
||||||
|
fn convert_into(self, env: &mut JNIEnv<'a>) -> Result<Self::ResultType, BridgeLayerError> {
|
||||||
|
let Self {
|
||||||
|
connection_reused,
|
||||||
|
reconnect_count,
|
||||||
|
ip_type,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// reconnect count as i32
|
||||||
|
let reconnect_count_i32: i32 = reconnect_count.try_into().expect("within i32 range");
|
||||||
|
|
||||||
|
// ip type as code
|
||||||
|
let ip_type_byte = ip_type as i8;
|
||||||
|
|
||||||
|
let class = {
|
||||||
|
const RESPONSE_CLASS: &str =
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::DebugInfo);
|
||||||
|
get_preloaded_class(env, RESPONSE_CLASS)
|
||||||
|
.transpose()
|
||||||
|
.unwrap_or_else(|| env.find_class(RESPONSE_CLASS))?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(new_object(
|
||||||
|
env,
|
||||||
|
class,
|
||||||
|
jni_args!((
|
||||||
|
connection_reused => boolean,
|
||||||
|
reconnect_count_i32 => int,
|
||||||
|
ip_type_byte => byte
|
||||||
|
) -> void),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ResultTypeInfo<'a> for ResponseAndDebugInfo {
|
||||||
|
type ResultType = JObject<'a>;
|
||||||
|
|
||||||
|
fn convert_into(self, env: &mut JNIEnv<'a>) -> Result<Self::ResultType, BridgeLayerError> {
|
||||||
|
let Self {
|
||||||
|
response,
|
||||||
|
debug_info,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let response: JObject<'a> = response.convert_into(env)?;
|
||||||
|
let debug_info: JObject<'a> = debug_info.convert_into(env)?;
|
||||||
|
|
||||||
|
let class = {
|
||||||
|
const RESPONSE_CLASS: &str =
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::ResponseAndDebugInfo);
|
||||||
|
get_preloaded_class(env, RESPONSE_CLASS)
|
||||||
|
.transpose()
|
||||||
|
.unwrap_or_else(|| env.find_class(RESPONSE_CLASS))?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(new_object(
|
||||||
|
env,
|
||||||
|
class,
|
||||||
|
jni_args!((
|
||||||
|
response => org.signal.libsignal.net.ChatService::Response,
|
||||||
|
debug_info => org.signal.libsignal.net.ChatService::DebugInfo
|
||||||
|
) -> void),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts each element of `it` to a Java object, storing the result in an array.
|
/// Converts each element of `it` to a Java object, storing the result in an array.
|
||||||
///
|
///
|
||||||
/// `element_type_signature` should use [`jni_class_name`] if it's a plain class and
|
/// `element_type_signature` should use [`jni_class_name`] if it's a plain class and
|
||||||
@ -1344,6 +1464,15 @@ macro_rules! jni_result_type {
|
|||||||
(LookupResponse) => {
|
(LookupResponse) => {
|
||||||
jni::JObject<'local>
|
jni::JObject<'local>
|
||||||
};
|
};
|
||||||
|
(Response) => {
|
||||||
|
jni::JObject<'local>
|
||||||
|
};
|
||||||
|
(DebugInfo) => {
|
||||||
|
jni::JObject<'local>
|
||||||
|
};
|
||||||
|
(ResponseAndDebugInfo) => {
|
||||||
|
jni::JObject<'local>
|
||||||
|
};
|
||||||
(CiphertextMessage) => {
|
(CiphertextMessage) => {
|
||||||
jni::JavaCiphertextMessage<'local>
|
jni::JavaCiphertextMessage<'local>
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Copyright 2020-2021 Signal Messenger, LLC.
|
// Copyright 2020-2021 Signal Messenger, LLC.
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
//
|
//
|
||||||
|
use http::uri::InvalidUri;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -11,6 +12,7 @@ use jni::{JNIEnv, JavaVM};
|
|||||||
|
|
||||||
use attest::hsm_enclave::Error as HsmEnclaveError;
|
use attest::hsm_enclave::Error as HsmEnclaveError;
|
||||||
use device_transfer::Error as DeviceTransferError;
|
use device_transfer::Error as DeviceTransferError;
|
||||||
|
use libsignal_net::chat::ChatServiceError;
|
||||||
use libsignal_net::infra::ws::{WebSocketConnectError, WebSocketServiceError};
|
use libsignal_net::infra::ws::{WebSocketConnectError, WebSocketServiceError};
|
||||||
use libsignal_protocol::*;
|
use libsignal_protocol::*;
|
||||||
use signal_crypto::Error as SignalCryptoError;
|
use signal_crypto::Error as SignalCryptoError;
|
||||||
@ -45,6 +47,8 @@ pub enum SignalJniError {
|
|||||||
Cdsi(CdsiError),
|
Cdsi(CdsiError),
|
||||||
Svr3(libsignal_net::svr3::Error),
|
Svr3(libsignal_net::svr3::Error),
|
||||||
WebSocket(#[from] WebSocketServiceError),
|
WebSocket(#[from] WebSocketServiceError),
|
||||||
|
ChatService(ChatServiceError),
|
||||||
|
InvalidUri(InvalidUri),
|
||||||
Timeout,
|
Timeout,
|
||||||
Bridge(BridgeLayerError),
|
Bridge(BridgeLayerError),
|
||||||
#[cfg(feature = "testing-fns")]
|
#[cfg(feature = "testing-fns")]
|
||||||
@ -90,6 +94,8 @@ impl fmt::Display for SignalJniError {
|
|||||||
#[cfg(feature = "signal-media")]
|
#[cfg(feature = "signal-media")]
|
||||||
SignalJniError::WebpSanitizeParse(e) => write!(f, "{}", e),
|
SignalJniError::WebpSanitizeParse(e) => write!(f, "{}", e),
|
||||||
SignalJniError::Cdsi(e) => write!(f, "{}", e),
|
SignalJniError::Cdsi(e) => write!(f, "{}", e),
|
||||||
|
SignalJniError::ChatService(e) => write!(f, "{}", e),
|
||||||
|
SignalJniError::InvalidUri(e) => write!(f, "{}", e),
|
||||||
SignalJniError::WebSocket(e) => write!(f, "{e}"),
|
SignalJniError::WebSocket(e) => write!(f, "{e}"),
|
||||||
SignalJniError::Timeout => write!(f, "timeout"),
|
SignalJniError::Timeout => write!(f, "timeout"),
|
||||||
SignalJniError::Svr3(e) => write!(f, "{}", e),
|
SignalJniError::Svr3(e) => write!(f, "{}", e),
|
||||||
@ -201,6 +207,18 @@ impl From<UsernameLinkError> for SignalJniError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InvalidUri> for SignalJniError {
|
||||||
|
fn from(e: InvalidUri) -> Self {
|
||||||
|
SignalJniError::InvalidUri(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChatServiceError> for SignalJniError {
|
||||||
|
fn from(e: ChatServiceError) -> Self {
|
||||||
|
SignalJniError::ChatService(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<IoError> for SignalJniError {
|
impl From<IoError> for SignalJniError {
|
||||||
fn from(e: IoError) -> SignalJniError {
|
fn from(e: IoError) -> SignalJniError {
|
||||||
Self::Io(e)
|
Self::Io(e)
|
||||||
|
@ -556,6 +556,13 @@ where
|
|||||||
}
|
}
|
||||||
SignalJniError::Svr3(_) => jni_class_name!(org.signal.libsignal.svr.SvrException),
|
SignalJniError::Svr3(_) => jni_class_name!(org.signal.libsignal.svr.SvrException),
|
||||||
|
|
||||||
|
SignalJniError::InvalidUri(_) => {
|
||||||
|
jni_class_name!(java.net.MalformedURLException)
|
||||||
|
}
|
||||||
|
SignalJniError::ChatService(_) => {
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatServiceException)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "testing-fns")]
|
#[cfg(feature = "testing-fns")]
|
||||||
SignalJniError::TestingError { exception_class } => exception_class,
|
SignalJniError::TestingError { exception_class } => exception_class,
|
||||||
};
|
};
|
||||||
@ -701,6 +708,10 @@ static PRELOADED_CLASSES: OnceCell<HashMap<&'static str, GlobalRef>> = OnceCell:
|
|||||||
const PRELOADED_CLASS_NAMES: &[&str] = &[
|
const PRELOADED_CLASS_NAMES: &[&str] = &[
|
||||||
jni_class_name!(org.signal.libsignal.net.CdsiLookupResponse::Entry),
|
jni_class_name!(org.signal.libsignal.net.CdsiLookupResponse::Entry),
|
||||||
jni_class_name!(org.signal.libsignal.net.CdsiLookupResponse),
|
jni_class_name!(org.signal.libsignal.net.CdsiLookupResponse),
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService),
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::Response),
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::DebugInfo),
|
||||||
|
jni_class_name!(org.signal.libsignal.net.ChatService::ResponseAndDebugInfo),
|
||||||
jni_class_name!(org.signal.libsignal.internal.TestingException),
|
jni_class_name!(org.signal.libsignal.internal.TestingException),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -257,11 +257,11 @@ pub struct ResponseAndDebugInfo {
|
|||||||
bridge_handle!(Chat, clone = false);
|
bridge_handle!(Chat, clone = false);
|
||||||
bridge_handle!(HttpRequest, clone = false);
|
bridge_handle!(HttpRequest, clone = false);
|
||||||
|
|
||||||
#[cfg(feature = "node")]
|
#[cfg(any(feature = "node", feature = "jni"))]
|
||||||
/// Newtype wrapper for implementing [`TryFrom`]`
|
/// Newtype wrapper for implementing [`TryFrom`]`
|
||||||
struct HttpMethod(http::Method);
|
struct HttpMethod(http::Method);
|
||||||
|
|
||||||
#[cfg(feature = "node")]
|
#[cfg(any(feature = "node", feature = "jni"))]
|
||||||
impl TryFrom<String> for HttpMethod {
|
impl TryFrom<String> for HttpMethod {
|
||||||
type Error = <http::Method as FromStr>::Err;
|
type Error = <http::Method as FromStr>::Err;
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
@ -269,7 +269,7 @@ impl TryFrom<String> for HttpMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn HttpRequest_new(
|
fn HttpRequest_new(
|
||||||
method: AsType<HttpMethod, String>,
|
method: AsType<HttpMethod, String>,
|
||||||
path: String,
|
path: String,
|
||||||
@ -286,7 +286,7 @@ fn HttpRequest_new(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn_void(ffi = false, jni = false)]
|
#[bridge_fn_void(ffi = false)]
|
||||||
fn HttpRequest_add_header(
|
fn HttpRequest_add_header(
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
name: AsType<HeaderName, String>,
|
name: AsType<HeaderName, String>,
|
||||||
@ -298,7 +298,7 @@ fn HttpRequest_add_header(
|
|||||||
(*guard).append(header_key, header_value);
|
(*guard).append(header_key, header_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn ChatService_new(
|
fn ChatService_new(
|
||||||
connection_manager: &ConnectionManager,
|
connection_manager: &ConnectionManager,
|
||||||
username: String,
|
username: String,
|
||||||
@ -317,12 +317,12 @@ fn ChatService_new(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_io(TokioAsyncContext, ffi = false, jni = false)]
|
#[bridge_io(TokioAsyncContext, ffi = false)]
|
||||||
async fn ChatService_disconnect(chat: &Chat) {
|
async fn ChatService_disconnect(chat: &Chat) {
|
||||||
chat.service.disconnect().await
|
chat.service.disconnect().await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_io(TokioAsyncContext, ffi = false, jni = false)]
|
#[bridge_io(TokioAsyncContext, ffi = false)]
|
||||||
async fn ChatService_unauth_send(
|
async fn ChatService_unauth_send(
|
||||||
chat: &Chat,
|
chat: &Chat,
|
||||||
http_request: &HttpRequest,
|
http_request: &HttpRequest,
|
||||||
@ -340,7 +340,7 @@ async fn ChatService_unauth_send(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_io(TokioAsyncContext, ffi = false, jni = false)]
|
#[bridge_io(TokioAsyncContext, ffi = false)]
|
||||||
async fn ChatService_unauth_send_and_debug(
|
async fn ChatService_unauth_send_and_debug(
|
||||||
chat: &Chat,
|
chat: &Chat,
|
||||||
http_request: &HttpRequest,
|
http_request: &HttpRequest,
|
||||||
|
@ -11,7 +11,7 @@ use libsignal_protocol::{Aci, Pni};
|
|||||||
use nonzero_ext::nonzero;
|
use nonzero_ext::nonzero;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::net::{HttpRequest, TokioAsyncContext};
|
use crate::net::{HttpRequest, ResponseAndDebugInfo, TokioAsyncContext};
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
@ -48,12 +48,12 @@ fn TESTING_CdsiLookupErrorConvert() -> Result<(), LookupError> {
|
|||||||
Err(LookupError::ParseError)
|
Err(LookupError::ParseError)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatServiceErrorConvert() -> Result<(), ChatServiceError> {
|
fn TESTING_ChatServiceErrorConvert() -> Result<(), ChatServiceError> {
|
||||||
Err(ChatServiceError::Timeout)
|
Err(ChatServiceError::Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatServiceResponseConvert(body_present: bool) -> Result<Response, ChatServiceError> {
|
fn TESTING_ChatServiceResponseConvert(body_present: bool) -> Result<Response, ChatServiceError> {
|
||||||
let body = match body_present {
|
let body = match body_present {
|
||||||
true => Some(b"content".to_vec().into_boxed_slice()),
|
true => Some(b"content".to_vec().into_boxed_slice()),
|
||||||
@ -64,13 +64,13 @@ fn TESTING_ChatServiceResponseConvert(body_present: bool) -> Result<Response, Ch
|
|||||||
headers.append(http::header::FORWARDED, HeaderValue::from_static("1.1.1.1"));
|
headers.append(http::header::FORWARDED, HeaderValue::from_static("1.1.1.1"));
|
||||||
Ok(Response {
|
Ok(Response {
|
||||||
status: StatusCode::OK,
|
status: StatusCode::OK,
|
||||||
message: None,
|
message: Some("OK".to_string()),
|
||||||
body,
|
body,
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatServiceDebugInfoConvert() -> Result<DebugInfo, ChatServiceError> {
|
fn TESTING_ChatServiceDebugInfoConvert() -> Result<DebugInfo, ChatServiceError> {
|
||||||
Ok(DebugInfo {
|
Ok(DebugInfo {
|
||||||
connection_reused: true,
|
connection_reused: true,
|
||||||
@ -79,17 +79,26 @@ fn TESTING_ChatServiceDebugInfoConvert() -> Result<DebugInfo, ChatServiceError>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
|
fn TESTING_ChatServiceResponseAndDebugInfoConvert() -> Result<ResponseAndDebugInfo, ChatServiceError>
|
||||||
|
{
|
||||||
|
Ok(ResponseAndDebugInfo {
|
||||||
|
response: TESTING_ChatServiceResponseConvert(true)?,
|
||||||
|
debug_info: TESTING_ChatServiceDebugInfoConvert()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatRequestGetMethod(request: &HttpRequest) -> String {
|
fn TESTING_ChatRequestGetMethod(request: &HttpRequest) -> String {
|
||||||
request.method.to_string()
|
request.method.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatRequestGetPath(request: &HttpRequest) -> String {
|
fn TESTING_ChatRequestGetPath(request: &HttpRequest) -> String {
|
||||||
request.path.to_string()
|
request.path.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatRequestGetHeaderValue(request: &HttpRequest, header_name: String) -> String {
|
fn TESTING_ChatRequestGetHeaderValue(request: &HttpRequest, header_name: String) -> String {
|
||||||
request
|
request
|
||||||
.headers
|
.headers
|
||||||
@ -102,7 +111,7 @@ fn TESTING_ChatRequestGetHeaderValue(request: &HttpRequest, header_name: String)
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bridge_fn(ffi = false, jni = false)]
|
#[bridge_fn(ffi = false)]
|
||||||
fn TESTING_ChatRequestGetBody(request: &HttpRequest) -> Option<Vec<u8>> {
|
fn TESTING_ChatRequestGetBody(request: &HttpRequest) -> Option<Vec<u8>> {
|
||||||
request.body.clone().map(|b| b.to_vec())
|
request.body.clone().map(|b| b.to_vec())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user