Compare commits

...

89 commits
1.0.1 ... main

Author SHA1 Message Date
renovate[bot]
4df5c18721 Update dependency gradle to v8.10 2024-08-14 15:08:01 +00:00
renovate[bot]
45e38e0e91 Update Rust crate tokio to v1.39.2 2024-07-27 12:43:08 +00:00
renovate[bot]
160b6565f5 Update Rust crate tokio to v1.39.1 2024-07-23 18:08:21 +00:00
renovate[bot]
6961ffaebb Update Rust crate tokio to v1.39.0 2024-07-23 15:02:12 +00:00
renovate[bot]
8944b04def Update Rust crate tokio to v1.38.1 2024-07-16 19:52:24 +00:00
renovate[bot]
b4c5d8fde6 Update dependency gradle to v8.9 2024-07-11 18:18:10 +00:00
renovate[bot]
d4d275c466 Update Rust crate reqwest to v0.12.5 2024-06-17 15:58:31 +00:00
renovate[bot]
b63ad1eeca Update dependency gradle to v8.8 2024-06-01 00:03:04 +00:00
renovate[bot]
912e312361 Update Rust crate tokio to v1.38.0 2024-05-30 23:18:18 +00:00
renovate[bot]
f0492d094e Update Rust crate reqwest to 0.12.4 2024-04-19 19:30:08 +00:00
renovate[bot]
44a3794c6c Update Rust crate reqwest to 0.12.3 2024-04-05 16:44:31 +00:00
Kavin
c1954425a8
Bump version. 2024-04-02 04:09:44 +01:00
renovate[bot]
1b29ee314c Update Rust crate tokio to 1.37.0 2024-03-28 20:18:50 +00:00
renovate[bot]
6f1f4f7357 Update plugin com.github.johnrengelman.shadow to v8 2024-03-28 20:12:04 +00:00
Kavin
16c1e705db
Update workflow to properly build. 2024-03-28 20:09:59 +00:00
Kavin
b594d71860
Update renovate config. 2024-03-28 20:06:58 +00:00
Kavin
8af02b0b70
Add CI workflow for testing builds. 2024-03-28 20:06:49 +00:00
Kavin
f2b86d2dde
Update publish workflow to use java 21. 2024-03-28 20:05:36 +00:00
Kavin
1423005cd1
Update gradle wrapper to 8.7. 2024-03-28 20:04:04 +00:00
renovate[bot]
d5b8bd46ae Update Rust crate reqwest to 0.12.2 2024-03-25 16:21:57 +00:00
renovate[bot]
12b7aa3a46 Update Rust crate reqwest to 0.12.1 2024-03-24 06:22:34 +00:00
renovate[bot]
452519987a Update Rust crate reqwest to 0.12.0 2024-03-20 19:17:34 +00:00
renovate[bot]
de6f9e7c90 Update Rust crate reqwest to 0.11.27 2024-03-19 22:18:24 +00:00
renovate[bot]
f6d5000750 Update Rust crate reqwest to 0.11.26 2024-03-12 19:56:37 +00:00
renovate[bot]
ea12d11343 Update Rust crate reqwest to 0.11.25 2024-03-09 02:48:30 +00:00
renovate[bot]
5a0e34ed3e Update Rust crate tokio to 1.36.0 2024-02-02 20:06:21 +00:00
renovate[bot]
0b58ffbb76 Update Rust crate reqwest to 0.11.24 2024-01-31 18:26:34 +00:00
renovate[bot]
bdedb3e79b Update Rust crate tokio to 1.35.1 2023-12-20 01:45:43 +00:00
renovate[bot]
6ad484fcec Update Rust crate reqwest to 0.11.23 2023-12-18 22:54:14 +00:00
Kavin
54ea93a1a4
Bump version. 2023-12-14 20:57:57 +00:00
Kavin
5408d34a22
Refactor to send request and response without channels. 2023-12-14 20:37:19 +00:00
renovate[bot]
bfd4ac68e6 Update Rust crate tokio to 1.35.0 2023-12-09 06:49:11 +00:00
renovate[bot]
36106ff373 Update Rust crate tokio to 1.34.0 2023-11-29 22:07:35 +00:00
renovate[bot]
7e08097657 Update actions/setup-java action to v4 2023-11-29 17:13:26 +00:00
renovate[bot]
2aa53ae682 Update Rust crate reqwest to 0.11.22 2023-10-03 14:01:45 +01:00
renovate[bot]
207078caee Update Rust crate reqwest to 0.11.21 2023-10-03 01:37:17 +01:00
renovate[bot]
52bb8f76e4 Update actions/checkout action to v4 2023-09-04 16:44:44 +00:00
Kavin
e1088e76bc
Refactor and make fully async with the usage of channels 2023-08-31 16:24:42 +01:00
Kavin
f6cdafe9f5
Bump version. 2023-08-31 16:22:55 +01:00
Kavin
330a2dd8f9
Add time feature since connect_timeout requires it 2023-08-31 13:18:14 +01:00
Kavin
b3f224e9e9
Bump version. 2023-08-31 13:13:19 +01:00
Kavin
244119c1c7
Update lockfile. 2023-08-31 13:12:42 +01:00
Kavin
dd7fb60064
Add timeouts to client. 2023-08-31 13:12:24 +01:00
renovate[bot]
122270a92a Update Rust crate reqwest to 0.11.20 2023-08-24 00:09:00 +01:00
renovate[bot]
9d5b9ae4b8 Update Rust crate reqwest to 0.11.19 2023-08-21 21:05:51 +01:00
Kavin
88630df150
Bump version. 2023-08-21 09:35:55 +01:00
Kavin
c40f40ce18
Remove features from tokio. 2023-08-21 09:35:41 +01:00
Kavin
2c81e10652
Add support set basic proxy authentication. 2023-08-21 09:27:38 +01:00
renovate[bot]
31bfff42e3 Update Rust crate tokio to 1.32.0 2023-08-17 01:06:57 +01:00
Kavin
b48dab9a97
Bump version. 2023-08-12 18:58:57 +01:00
renovate[bot]
4eafc585a2 Update Rust crate tokio to 1.31.0 2023-08-12 18:48:40 +01:00
Kavin
2d2a00e50b
Merge pull request #21 from TeamPiped/sweep/enable-lto
Enable LTO for release build in Cargo.toml
2023-08-11 21:52:45 +01:00
sweep-ai[bot]
3d7d8456d3
Added LTO to release build 2023-08-11 11:06:18 +00:00
renovate[bot]
12b61f0967 Update Rust crate tokio to 1.30.0 2023-08-09 21:52:48 +01:00
Kavin
ea03b98c3e
Remove once_cell and use std's OnceLock 2023-08-05 19:09:17 +01:00
Kavin
2af849dc8f
Bump version. 2023-08-04 22:33:53 +01:00
Kavin
b7b9c918a8
Fix formatting. 2023-08-04 17:35:04 +01:00
Cherry
70b4734f3d
Properly close fileoutput stream 2023-08-04 08:36:12 -07:00
Kavin
7d686cff56
Bump version 2023-08-04 01:10:47 +01:00
Kavin
41073ce8d5
Merge pull request #16 from MolotovCherry/main
windows support
2023-08-04 01:10:01 +01:00
Cherryleafroad
fdca2569ec
Update windows cross target 2023-08-03 17:02:48 -07:00
Cherryleafroad
e41411d9ec
windows support 2023-08-03 15:26:25 -07:00
Kavin
29cf27d24f
Add support for setting up a proxy. 2023-07-04 19:04:48 +01:00
Kavin
da5a77e2f0
Bump version. 2023-06-30 13:47:32 +01:00
Kavin
eb46230403
Use a CompletableFuture instead of blocking the JVM thread. 2023-06-30 13:45:14 +01:00
renovate[bot]
75cfd8dadd Update Rust crate tokio to 1.29.1 2023-06-29 23:53:44 +01:00
renovate[bot]
4d463b5a3e Update Rust crate tokio to 1.29.0 2023-06-28 01:15:44 +01:00
Kavin
3ff902794a
Bump version. 2023-06-06 00:19:35 +01:00
Kavin
1c58bc2dba
Cleanup tempDir file creation. 2023-06-06 00:16:17 +01:00
Kavin
4480826b54
Update lockfile. 2023-06-06 00:15:17 +01:00
renovate[bot]
a17bb54b12 Update Rust crate once_cell to 1.18.0 2023-06-04 17:10:43 +00:00
renovate[bot]
5a2be309e1 Update Rust crate once_cell to 1.17.2 2023-05-29 14:57:57 +01:00
renovate[bot]
34d14f3460 Update Rust crate tokio to 1.28.2 2023-05-28 02:22:56 +01:00
renovate[bot]
ea32df97aa Update Rust crate reqwest to 0.11.18 2023-05-16 23:01:56 +01:00
renovate[bot]
1f27a6bbc6 Update Rust crate tokio to 1.28.1 2023-05-10 18:01:09 +01:00
renovate[bot]
6a03e66cdf Update Rust crate reqwest to 0.11.17 2023-04-28 18:12:12 +01:00
renovate[bot]
a8a4f7a604 Update Rust crate tokio to 1.28.0 2023-04-26 02:11:35 +01:00
Kavin
5339ed013f
Bump version. 2023-04-19 20:06:28 +01:00
Kavin
e02a92dcdb
Fix compilation and a runtime error. 2023-04-19 19:44:04 +01:00
Kavin
d2ca77c625
Remove WIP 2023-03-31 13:54:45 +01:00
renovate[bot]
ddc8e92cea Update Rust crate tokio to 1.27.0 2023-03-28 02:58:48 +00:00
renovate[bot]
ede8ed751d Update Rust crate reqwest to 0.11.16 2023-03-27 21:53:00 +00:00
renovate[bot]
21a8b2a896 Update Rust crate jni to 0.21.1 2023-03-25 19:21:00 +00:00
renovate[bot]
ba6e6f7b49 Update Rust crate tokio to 1.26.0 2023-03-25 19:20:49 +00:00
renovate[bot]
283d4f8da9 Update Rust crate reqwest to 0.11.15 2023-03-25 17:02:10 +00:00
Kavin
ea674a6f80
Add renovate config. 2023-03-25 16:04:54 +00:00
Kavin
2162cbe153
Migrate lazy_static to once_cell. 2023-03-25 15:59:07 +00:00
Kavin
0ad3b94bc3
Fix crash when there's no response body. 2023-03-04 20:30:09 +00:00
Kavin
be5ff40c97
Remove unnecessary boilerplate code. 2023-03-04 04:22:36 +00:00
14 changed files with 948 additions and 468 deletions

30
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 21 ]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
reqwest-jni
- run: cargo install cross
- name: set up JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: zulu
cache: "gradle"
- name: Run Build
run: ./gradlew shadowJar

View file

@ -12,17 +12,18 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
reqwest-jni
- run: cargo install cross
- name: set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
java-version: 21
distribution: zulu
check-latest: true
cache: "gradle"
- name: Save Private Key

View file

@ -1,2 +1,2 @@
# reqwest4j
WIP Java bindings to Reqwest with jni-rs for use in Piped's backend!
Java bindings to Reqwest with jni-rs for use in Piped's backend!

View file

@ -3,7 +3,7 @@ plugins {
id "maven-publish"
id "signing"
id "fr.stardustenterprises.rust.importer" version "3.2.5"
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
repositories {
@ -17,14 +17,14 @@ dependencies {
// javac -h
tasks.register('generateJniHeaders', JavaCompile) {
classpath = sourceSets.main.compileClasspath
destinationDir file("${buildDir}/generated/jni")
destinationDir file("${layout.buildDirectory}/generated/jni")
source = sourceSets.main.java
options.compilerArgs += [
'-h', file("${buildDir}/generated/jni"),
'-d', file("${buildDir}/generated/jni-classes"),
'-h', file("${layout.buildDirectory}/generated/jni"),
'-d', file("${layout.buildDirectory}/generated/jni-classes"),
]
doLast {
delete file("${buildDir}/generated/jni-classes")
delete file("${layout.buildDirectory}/generated/jni-classes")
}
}
@ -36,6 +36,8 @@ rustImport {
java {
withSourcesJar()
withJavadocJar()
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
signing {
@ -43,9 +45,7 @@ signing {
}
group = 'rocks.kavin'
version = '1.0.1'
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
version = '1.0.14'
publishing {
repositories {

Binary file not shown.

View file

@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://downloads.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

34
gradlew vendored
View file

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -83,10 +85,9 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -133,10 +134,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \

22
gradlew.bat vendored
View file

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail

17
renovate.json Normal file
View file

@ -0,0 +1,17 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
"group:recommended"
],
"ignorePresets": [
":prHourlyLimit2"
],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true,
"platformAutomerge": true
}
]
}

972
reqwest-jni/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,10 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
jni = "0.20.0"
reqwest = {version = "0.11.14", features = ["rustls-tls", "stream", "brotli", "gzip"], default-features = false}
tokio = {version = "1.24.2", features = ["full"]}
lazy_static = "1.4.0"
jni = "0.21.1"
reqwest = {version = "0.12.4", features = ["rustls-tls", "stream", "brotli", "gzip", "socks"], default-features = false}
tokio = {version = "1.37.0", features = ["rt-multi-thread", "time"], default-features = false}
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true

View file

@ -8,4 +8,5 @@ rust {
targets += target("aarch64-unknown-linux-gnu", "libreqwest.so")
targets += target("x86_64-unknown-linux-gnu", "libreqwest.so")
targets += target("x86_64-pc-windows-gnu", "libreqwest.dll")
}

View file

@ -1,61 +1,115 @@
use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use jni::objects::{JByteArray, JClass, JMap, JObject, JString};
use jni::sys::jobject;
use jni::JNIEnv;
use jni::objects::{JClass, JMap, JObject, JString};
use jni::sys::{jbyteArray, jobject};
use lazy_static::lazy_static;
use reqwest::{Client, Method, Url};
use tokio::runtime::Runtime;
pub fn add(left: usize, right: usize) -> usize {
left + right
}
static RUNTIME: OnceLock<Runtime> = OnceLock::new();
static CLIENT: OnceLock<Client> = OnceLock::new();
lazy_static! {
static ref RUNTIME: Runtime = Runtime::new().unwrap();
static ref CLIENT: Client = Client::builder()
.user_agent("Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0")
#[no_mangle]
pub extern "system" fn Java_rocks_kavin_reqwest4j_ReqwestUtils_init(
mut env: JNIEnv,
_: JClass,
proxy: JString,
user: JString,
pass: JString,
) {
let builder = Client::builder()
.user_agent("Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0");
let builder = match env.get_string(&proxy) {
Ok(proxy) => {
let proxy = proxy.to_str().unwrap();
let proxy = reqwest::Proxy::all(proxy).unwrap();
let proxy = match env.get_string(&user) {
Ok(user) => {
let user = user.to_str().unwrap();
let pass = env.get_string(&pass).unwrap();
let pass = pass.to_str().unwrap();
proxy.basic_auth(user, pass)
}
Err(_) => proxy,
};
builder.proxy(proxy)
}
Err(_) => builder,
};
let client = builder
// timeout for establishing connection
.connect_timeout(Duration::from_secs(10))
// timeout for entire request, till body is read
.timeout(Duration::from_secs(30))
.build()
.unwrap();
CLIENT.set(client).unwrap();
RUNTIME.set(Runtime::new().unwrap()).unwrap();
}
#[no_mangle]
pub extern "system" fn Java_rocks_kavin_reqwest4j_ReqwestUtils_fetch(
env: JNIEnv,
mut env: JNIEnv,
_: JClass,
url: JString,
method: JString,
body: jbyteArray,
body: JByteArray,
headers: JObject,
) -> jobject {
// set method, url, body, headers
let method = Method::from_bytes(env.get_string(method).unwrap().to_bytes()).unwrap();
let method = Method::from_bytes(env.get_string(&method).unwrap().to_bytes()).unwrap();
let url = &env.get_string(url).unwrap();
let url = &env.get_string(&url).unwrap();
let url = url.to_str();
if url.is_err() {
env.throw_new("java/lang/IllegalArgumentException", "Invalid URL provided, couldn't get string as UTF-8").unwrap();
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid URL provided, couldn't get string as UTF-8",
)
.unwrap();
return JObject::null().into_raw();
}
let url = Url::parse(url.unwrap()).unwrap();
let body = env.convert_byte_array(body).unwrap_or_default();
let headers: JMap = JMap::from_env(&env, headers).unwrap();
let headers = headers.iter().unwrap().fold(HashMap::new(), |mut headers, (key, value)| {
let java_headers: JMap = JMap::from_env(&mut env, &headers).unwrap();
let mut java_headers = java_headers.iter(&mut env).unwrap();
let mut headers = HashMap::new();
while let Some((key, value)) = java_headers.next(&mut env).unwrap() {
headers.insert(
env.get_string(JString::from(key)).unwrap().to_str().unwrap().to_string(),
env.get_string(JString::from(value)).unwrap().to_str().unwrap().to_string(),
env.get_string(&JString::from(key))
.unwrap()
.to_str()
.unwrap()
.to_string(),
env.get_string(&JString::from(value))
.unwrap()
.to_str()
.unwrap()
.to_string(),
);
headers
});
}
let request = CLIENT.request(method, url);
let client = CLIENT.get();
let request = headers.into_iter().fold(request, |request, (key, value)| {
request.header(key, value)
});
if client.is_none() {
env.throw_new("java/lang/IllegalStateException", "Client not initialized")
.unwrap();
return JObject::null().into_raw();
}
let client = client.unwrap();
let request = client.request(method, url);
let request = headers
.into_iter()
.fold(request, |request, (key, value)| request.header(key, value));
let request = if body.is_empty() {
request
@ -63,52 +117,113 @@ pub extern "system" fn Java_rocks_kavin_reqwest4j_ReqwestUtils_fetch(
request.body(body)
};
// send request
let response = RUNTIME.block_on(async {
request.send().await.unwrap()
});
// `JNIEnv` cannot be sent between threads safely
let jvm = env.get_java_vm().unwrap();
let jvm = Arc::new(jvm);
// get response
let status = response.status().as_u16() as i32;
// create CompletableFuture
let _future = env
.new_object("java/util/concurrent/CompletableFuture", "()V", &[])
.unwrap();
let future = env.new_global_ref(&_future).unwrap();
let future = Arc::new(future);
let headers = env.new_object("java/util/HashMap", "()V", &[]).unwrap();
let headers: JMap = JMap::from_env(&env, headers).unwrap();
let runtime = RUNTIME.get().unwrap();
response.headers().iter().for_each(|(key, value)| {
let key = env.new_string(key.as_str()).unwrap();
let value = env.new_string(value.to_str().unwrap()).unwrap();
headers.put(JObject::from(key), JObject::from(value)).unwrap();
});
// send request in a async task
{
let jvm = Arc::clone(&jvm);
let future = Arc::clone(&future);
let final_url = response.url().to_string();
let final_url = env.new_string(final_url).unwrap();
runtime.spawn(async move {
// send request
let response = request.send().await;
let body = RUNTIME.block_on(async {
response.bytes().await.unwrap().to_vec()
});
match response {
Ok(response) => {
// get response
let status = response.status().as_u16() as i32;
let final_url = response.url().to_string();
let body = env.byte_array_from_slice(&body).unwrap();
let body = unsafe { JObject::from_raw(body) };
let response_headers = response.headers().clone();
// return response
let response = env.new_object("rocks/kavin/reqwest4j/Response", "(ILjava/util/Map;[BLjava/lang/String;)V", &[
status.into(),
headers.into(),
body.into(),
final_url.into(),
]).unwrap();
let body = response.bytes().await.unwrap_or_default().to_vec();
response.into_raw()
}
// send response in a blocking task
runtime.spawn_blocking(move || {
let mut env = jvm.attach_current_thread().unwrap();
#[cfg(test)]
mod tests {
use super::*;
let final_url = env.new_string(final_url).unwrap();
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
let body = env.byte_array_from_slice(&body).unwrap();
let headers = env.new_object("java/util/HashMap", "()V", &[]).unwrap();
let headers: JMap = JMap::from_env(&mut env, &headers).unwrap();
response_headers.iter().for_each(|(key, value)| {
let key = env.new_string(key.as_str()).unwrap();
let value = env.new_string(value.to_str().unwrap()).unwrap();
headers
.put(&mut env, &JObject::from(key), &JObject::from(value))
.unwrap();
});
// return response to CompletableFuture
let response = env
.new_object(
"rocks/kavin/reqwest4j/Response",
"(ILjava/util/Map;[BLjava/lang/String;)V",
&[
status.into(),
(&headers).into(),
(&body).into(),
(&final_url).into(),
],
)
.unwrap();
let future = future.as_obj();
env.call_method(
future,
"complete",
"(Ljava/lang/Object;)Z",
&[(&response).into()],
)
.unwrap();
});
}
Err(error) => {
// send error in a blocking task
runtime.spawn_blocking(move || {
let mut env = jvm.attach_current_thread().unwrap();
let error = error.to_string();
let error = env.new_string(error).unwrap();
// create Exception
let exception = env
.new_object(
"java/lang/Exception",
"(Ljava/lang/String;)V",
&[(&error).into()],
)
.unwrap();
let future = future.as_obj();
// pass error to CompletableFuture
env.call_method(
future,
"completeExceptionally",
"(Ljava/lang/Throwable;)Z",
&[(&exception).into()],
)
.unwrap();
});
}
}
});
}
_future.into_raw()
}

View file

@ -1,42 +1,61 @@
package rocks.kavin.reqwest4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class ReqwestUtils {
static {
String arch;
String arch = switch (System.getProperty("os.arch")) {
case "aarch64" -> "aarch64";
case "amd64" -> "x86_64";
default -> throw new RuntimeException("Unsupported architecture");
};
switch (System.getProperty("os.arch")) {
case "aarch64":
arch = "aarch64";
break;
case "amd64":
arch = "x86_64";
break;
default:
throw new RuntimeException("Unsupported architecture");
String os = System.getProperty("os.name").toLowerCase();
String extension;
String native_folder;
if (os.contains("win")) {
extension = ".dll";
native_folder = "windows";
} else if (os.contains("linux")) {
extension = ".so";
native_folder = "linux";
} else {
throw new RuntimeException("OS not supported");
}
String fileName =
System.getProperty("java.io.tmpdir") +
File.separatorChar +
"libreqwest_" + System.currentTimeMillis() + ".so";
File nativeFile;
final var cl = ReqwestUtils.class.getClassLoader();
try (var stream = cl.getResourceAsStream("META-INF/natives/linux/" + arch + "/libreqwest.so")) {
stream.transferTo(new java.io.FileOutputStream(fileName));
try {
nativeFile = File.createTempFile("libreqwest", extension);
nativeFile.deleteOnExit();
} catch (IOException e) {
throw new RuntimeException(e);
}
System.load(fileName);
final var cl = ReqwestUtils.class.getClassLoader();
try (
var stream = cl.getResourceAsStream("META-INF/natives/" + native_folder + "/" + arch + "/libreqwest" + extension);
var fileOutputStream = new FileOutputStream(nativeFile)
) {
stream.transferTo(fileOutputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.load(nativeFile.getAbsolutePath());
}
public static native Response fetch(String url, String method, byte[] body,
Map<String, String> headers);
public static native void init(String proxy, String user, String pass);
public static native CompletableFuture<Response> fetch(String url, String method, byte[] body,
Map<String, String> headers);
}