@pigeonmal/react-native-nitro-fetch 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroFetch.podspec +30 -0
- package/android/CMakeLists.txt +70 -0
- package/android/build.gradle +130 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +72 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +58 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NativeStorage.kt +102 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetch.kt +94 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +331 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchPackage.kt +22 -0
- package/ios/FetchCache.swift +56 -0
- package/ios/NativeStorage.swift +61 -0
- package/ios/NitroAutoPrefetcher.swift +45 -0
- package/ios/NitroBootstrap.mm +27 -0
- package/ios/NitroFetch.swift +9 -0
- package/ios/NitroFetchClient.swift +230 -0
- package/lib/module/NitroFetch.nitro.js +4 -0
- package/lib/module/NitroFetch.nitro.js.map +1 -0
- package/lib/module/NitroInstances.js +8 -0
- package/lib/module/NitroInstances.js.map +1 -0
- package/lib/module/fetch.js +522 -0
- package/lib/module/fetch.js.map +1 -0
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/type.js +2 -0
- package/lib/module/type.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts +48 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -0
- package/lib/typescript/src/NitroInstances.d.ts +5 -0
- package/lib/typescript/src/NitroInstances.d.ts.map +1 -0
- package/lib/typescript/src/fetch.d.ts +28 -0
- package/lib/typescript/src/fetch.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/type.d.ts +4 -0
- package/lib/typescript/src/type.d.ts.map +1 -0
- package/nitro.json +25 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +54 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +96 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.cpp +49 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JNitroHeader.hpp +57 -0
- package/nitrogen/generated/android/c++/JNitroRequest.hpp +100 -0
- package/nitrogen/generated/android/c++/JNitroRequestMethod.hpp +74 -0
- package/nitrogen/generated/android/c++/JNitroResponse.hpp +102 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchSpec.kt +52 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroHeader.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequestMethod.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroResponse.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/nitrofetchOnLoad.kt +35 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.cmake +85 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrofetchOnLoad.cpp +64 -0
- package/nitrogen/generated/android/nitrofetchOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/NitroFetch+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +90 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +321 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +69 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.mm +49 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.swift +55 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +85 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +103 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.hpp +75 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_NitroResponse.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +51 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +145 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +51 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +161 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchSpec.swift +49 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchSpec_cxx.swift +126 -0
- package/nitrogen/generated/ios/swift/NitroHeader.swift +46 -0
- package/nitrogen/generated/ios/swift/NitroRequest.swift +206 -0
- package/nitrogen/generated/ios/swift/NitroRequestMethod.swift +60 -0
- package/nitrogen/generated/ios/swift/NitroResponse.swift +162 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +69 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/NitroHeader.hpp +71 -0
- package/nitrogen/generated/shared/c++/NitroRequest.hpp +98 -0
- package/nitrogen/generated/shared/c++/NitroRequestMethod.hpp +96 -0
- package/nitrogen/generated/shared/c++/NitroResponse.hpp +99 -0
- package/package.json +162 -0
- package/src/NitroFetch.nitro.ts +67 -0
- package/src/NitroInstances.ts +14 -0
- package/src/fetch.ts +603 -0
- package/src/index.tsx +17 -0
- package/src/type.ts +3 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "NitroFetch"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "http://google.com.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
s.source_files = [
|
|
18
|
+
"ios/**/*.{swift}",
|
|
19
|
+
"ios/**/*.{m,mm}",
|
|
20
|
+
"cpp/**/*.{hpp,cpp}",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
s.dependency 'React-jsi'
|
|
24
|
+
s.dependency 'React-callinvoker'
|
|
25
|
+
|
|
26
|
+
load 'nitrogen/generated/ios/NitroFetch+autolinking.rb'
|
|
27
|
+
add_nitrogen_files(s)
|
|
28
|
+
|
|
29
|
+
install_modules_dependencies(s)
|
|
30
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
project(nitrofetch)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME nitrofetch)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
# Define C++ library and add all sources
|
|
9
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
10
|
+
src/main/cpp/cpp-adapter.cpp
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Add Nitrogen specs :)
|
|
14
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/nitrofetch+autolinking.cmake)
|
|
15
|
+
|
|
16
|
+
# Set up local includes
|
|
17
|
+
include_directories("src/main/cpp" "../cpp")
|
|
18
|
+
|
|
19
|
+
# Allow Gradle to pass a CRONET_ROOT pointing to extracted headers/libs (see build.gradle prepareCronet task)
|
|
20
|
+
if (DEFINED CRONET_ROOT)
|
|
21
|
+
set(CRONET_ROOT_DIR ${CRONET_ROOT})
|
|
22
|
+
else()
|
|
23
|
+
set(CRONET_ROOT_DIR ${CMAKE_SOURCE_DIR}/cronet)
|
|
24
|
+
endif()
|
|
25
|
+
|
|
26
|
+
# Optional: include Cronet headers if present (placed by script under android/cronet/include)
|
|
27
|
+
if (EXISTS ${CRONET_ROOT_DIR}/include)
|
|
28
|
+
message(STATUS "Cronet headers base: ${CRONET_ROOT_DIR}/include")
|
|
29
|
+
include_directories(${CRONET_ROOT_DIR}/include)
|
|
30
|
+
# Some Cronet packages nest headers under include/cronet
|
|
31
|
+
if (EXISTS ${CRONET_ROOT_DIR}/include/cronet)
|
|
32
|
+
include_directories(${CRONET_ROOT_DIR}/include/cronet)
|
|
33
|
+
if (EXISTS ${CRONET_ROOT_DIR}/include/cronet/cronet_c.h)
|
|
34
|
+
message(STATUS "Found cronet_c.h: ${CRONET_ROOT_DIR}/include/cronet/cronet_c.h")
|
|
35
|
+
else()
|
|
36
|
+
message(WARNING "cronet_c.h not found under ${CRONET_ROOT_DIR}/include/cronet")
|
|
37
|
+
endif()
|
|
38
|
+
if (EXISTS ${CRONET_ROOT_DIR}/include/cronet/cronet.idl_c.h)
|
|
39
|
+
message(STATUS "Found cronet.idl_c.h: ${CRONET_ROOT_DIR}/include/cronet/cronet.idl_c.h")
|
|
40
|
+
else()
|
|
41
|
+
message(WARNING "cronet.idl_c.h not found under ${CRONET_ROOT_DIR}/include/cronet")
|
|
42
|
+
endif()
|
|
43
|
+
else()
|
|
44
|
+
message(WARNING "Cronet nested include dir not found: ${CRONET_ROOT_DIR}/include/cronet")
|
|
45
|
+
endif()
|
|
46
|
+
endif()
|
|
47
|
+
|
|
48
|
+
find_library(LOG_LIB log)
|
|
49
|
+
|
|
50
|
+
# Link all libraries together
|
|
51
|
+
target_link_libraries(
|
|
52
|
+
${PACKAGE_NAME}
|
|
53
|
+
${LOG_LIB}
|
|
54
|
+
android # <-- Android core
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
## Kotlin-based implementation only; no custom C++ headers forced.
|
|
58
|
+
|
|
59
|
+
# Optional: link Cronet if library file is present (drop-in via prepare script or Gradle task)
|
|
60
|
+
set(CRONET_LIB_DIR ${CRONET_ROOT_DIR}/libs/${ANDROID_ABI})
|
|
61
|
+
if (EXISTS ${CRONET_LIB_DIR})
|
|
62
|
+
file(GLOB CRONET_LIBS "${CRONET_LIB_DIR}/*cronet*.so")
|
|
63
|
+
if (CRONET_LIBS)
|
|
64
|
+
message(STATUS "Linking Cronet from ${CRONET_LIB_DIR}")
|
|
65
|
+
target_link_libraries(${PACKAGE_NAME} ${CRONET_LIBS})
|
|
66
|
+
target_compile_definitions(${PACKAGE_NAME} PRIVATE NITROFETCH_LINKS_CRONET=1)
|
|
67
|
+
else()
|
|
68
|
+
message(WARNING "Cronet libs not found in ${CRONET_LIB_DIR}")
|
|
69
|
+
endif()
|
|
70
|
+
endif()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.getExtOrDefault = {name ->
|
|
3
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['NitroFetch_' + name]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
dependencies {
|
|
12
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
13
|
+
// noinspection DifferentKotlinGradleVersion
|
|
14
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def reactNativeArchitectures() {
|
|
19
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
20
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
apply plugin: "com.android.library"
|
|
24
|
+
apply plugin: "kotlin-android"
|
|
25
|
+
apply from: '../nitrogen/generated/android/nitrofetch+autolinking.gradle'
|
|
26
|
+
|
|
27
|
+
// Do not apply React Gradle plugin in library to avoid RN codegen duplicating app classes
|
|
28
|
+
|
|
29
|
+
def getExtOrIntegerDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroFetch_" + name]).toInteger()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
android {
|
|
34
|
+
namespace "com.margelo.nitro.nitrofetch"
|
|
35
|
+
|
|
36
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
37
|
+
|
|
38
|
+
defaultConfig {
|
|
39
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
40
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
41
|
+
|
|
42
|
+
externalNativeBuild {
|
|
43
|
+
cmake {
|
|
44
|
+
cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
|
|
45
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
46
|
+
abiFilters (*reactNativeArchitectures())
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
externalNativeBuild {
|
|
52
|
+
cmake {
|
|
53
|
+
path "CMakeLists.txt"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
packagingOptions {
|
|
58
|
+
excludes = [
|
|
59
|
+
"**/libc++_shared.so",
|
|
60
|
+
"**/libfbjni.so",
|
|
61
|
+
"**/libjsi.so",
|
|
62
|
+
"**/libfolly_json.so",
|
|
63
|
+
"**/libfolly_runtime.so",
|
|
64
|
+
"**/libglog.so",
|
|
65
|
+
"**/libhermes.so",
|
|
66
|
+
"**/libhermes-executor-debug.so",
|
|
67
|
+
"**/libhermes_executor.so",
|
|
68
|
+
"**/libreactnative.so",
|
|
69
|
+
"**/libreactnativejni.so",
|
|
70
|
+
"**/libturbomodulejsijni.so",
|
|
71
|
+
"**/libreact_nativemodule_core.so",
|
|
72
|
+
"**/libjscexecutor.so"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
buildFeatures {
|
|
77
|
+
buildConfig true
|
|
78
|
+
prefab true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
buildTypes {
|
|
82
|
+
release {
|
|
83
|
+
minifyEnabled false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lintOptions {
|
|
88
|
+
disable "GradleCompatible"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
compileOptions {
|
|
92
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
93
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
sourceSets {
|
|
97
|
+
main {
|
|
98
|
+
java.srcDirs += [
|
|
99
|
+
"generated/java",
|
|
100
|
+
"generated/jni"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
repositories {
|
|
107
|
+
mavenCentral()
|
|
108
|
+
google()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
112
|
+
|
|
113
|
+
dependencies {
|
|
114
|
+
implementation "com.facebook.react:react-android"
|
|
115
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
116
|
+
implementation project(":react-native-nitro-modules")
|
|
117
|
+
// Provide org.chromium.net Java API for CronetEngine in Kotlin
|
|
118
|
+
// Cronet
|
|
119
|
+
api "com.google.android.gms:play-services-cronet:18.0.1"
|
|
120
|
+
implementation("com.squareup.okio:okio:2.9.0")
|
|
121
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
configurations {
|
|
126
|
+
cronetAar
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// No automatic fetching of Cronet headers/libs; library uses Cronet Java API only.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrofetch
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import org.json.JSONArray
|
|
6
|
+
import org.json.JSONObject
|
|
7
|
+
import java.util.concurrent.CompletableFuture
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
object AutoPrefetcher {
|
|
11
|
+
@Volatile private var initialized = false
|
|
12
|
+
private const val KEY_QUEUE = "nitrofetch_autoprefetch_queue"
|
|
13
|
+
private const val PREFS_NAME = "nitro_fetch_storage"
|
|
14
|
+
|
|
15
|
+
fun prefetchOnStart(app: Application) {
|
|
16
|
+
if (initialized) return
|
|
17
|
+
initialized = true
|
|
18
|
+
try {
|
|
19
|
+
val prefs = app.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
20
|
+
val raw = prefs.getString(KEY_QUEUE, null) ?: ""
|
|
21
|
+
if (raw.isEmpty()) return
|
|
22
|
+
val arr = JSONArray(raw)
|
|
23
|
+
for (i in 0 until arr.length()) {
|
|
24
|
+
val o = arr.optJSONObject(i) ?: continue
|
|
25
|
+
val url = o.optString("url", null) ?: continue
|
|
26
|
+
val prefetchKey = o.optString("prefetchKey", null) ?: continue
|
|
27
|
+
val headersObj = o.optJSONObject("headers") ?: JSONObject()
|
|
28
|
+
val headersList = mutableListOf<Pair<String, String>>()
|
|
29
|
+
headersObj.keys().forEachRemaining { k ->
|
|
30
|
+
headersList.add(k to headersObj.optString(k, ""))
|
|
31
|
+
}
|
|
32
|
+
// Ensure prefetchKey header is present
|
|
33
|
+
headersList.add("prefetchKey" to prefetchKey)
|
|
34
|
+
|
|
35
|
+
val headerObjs = headersList.map { (k, v) -> NitroHeader(k, v) }.toTypedArray()
|
|
36
|
+
val req = NitroRequest(
|
|
37
|
+
url = url,
|
|
38
|
+
method = null,
|
|
39
|
+
headers = headerObjs,
|
|
40
|
+
bodyString = null,
|
|
41
|
+
bodyBytes = null,
|
|
42
|
+
timeoutMs = null,
|
|
43
|
+
followRedirects = null
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// If already pending or fresh, skip starting a new one
|
|
47
|
+
if (FetchCache.getPending(prefetchKey) != null) continue
|
|
48
|
+
if (FetchCache.hasFreshResult(prefetchKey, 5_000L)) continue
|
|
49
|
+
|
|
50
|
+
val future = CompletableFuture<NitroResponse>()
|
|
51
|
+
FetchCache.setPending(prefetchKey, future)
|
|
52
|
+
NitroFetchClient.fetch(req,
|
|
53
|
+
onSuccess = { res ->
|
|
54
|
+
try {
|
|
55
|
+
FetchCache.complete(prefetchKey, res)
|
|
56
|
+
future.complete(res)
|
|
57
|
+
} catch (t: Throwable) {
|
|
58
|
+
FetchCache.completeExceptionally(prefetchKey, t)
|
|
59
|
+
future.completeExceptionally(t)
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
onFail = { err ->
|
|
63
|
+
FetchCache.completeExceptionally(prefetchKey, err)
|
|
64
|
+
future.completeExceptionally(err)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
} catch (_: Throwable) {
|
|
69
|
+
// ignore – prefetch-on-start is best-effort
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrofetch
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.CompletableFuture
|
|
4
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
5
|
+
|
|
6
|
+
data class CachedEntry(val response: NitroResponse, val timestampMs: Long)
|
|
7
|
+
|
|
8
|
+
object FetchCache {
|
|
9
|
+
private val pending = ConcurrentHashMap<String, CompletableFuture<NitroResponse>>()
|
|
10
|
+
private val results = ConcurrentHashMap<String, CachedEntry>()
|
|
11
|
+
|
|
12
|
+
fun getPending(key: String): CompletableFuture<NitroResponse>? = pending[key]
|
|
13
|
+
|
|
14
|
+
fun setPending(key: String, future: CompletableFuture<NitroResponse>) {
|
|
15
|
+
pending[key] = future
|
|
16
|
+
// Cleanup: remove pending entry when completed
|
|
17
|
+
future.whenComplete { _, _ ->
|
|
18
|
+
pending.remove(key)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fun complete(key: String, value: NitroResponse) {
|
|
23
|
+
results[key] = CachedEntry(value, System.currentTimeMillis())
|
|
24
|
+
pending[key]?.complete(value)
|
|
25
|
+
pending.remove(key)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun completeExceptionally(key: String, t: Throwable) {
|
|
29
|
+
pending[key]?.completeExceptionally(t)
|
|
30
|
+
pending.remove(key)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fun getResult(key: String): NitroResponse? {
|
|
34
|
+
val entry = results.remove(key) ?: return null
|
|
35
|
+
return entry.response
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fun getResultIfFresh(key: String, maxAgeMs: Long): NitroResponse? {
|
|
39
|
+
val entry = results.remove(key) ?: return null
|
|
40
|
+
val age = System.currentTimeMillis() - entry.timestampMs
|
|
41
|
+
return if (age <= maxAgeMs) entry.response else null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a fresh result exists WITHOUT consuming it.
|
|
46
|
+
* Used to check if we should skip starting a new prefetch.
|
|
47
|
+
*/
|
|
48
|
+
fun hasFreshResult(key: String, maxAgeMs: Long): Boolean {
|
|
49
|
+
val entry = results[key] ?: return false
|
|
50
|
+
val age = System.currentTimeMillis() - entry.timestampMs
|
|
51
|
+
return age <= maxAgeMs
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fun clear() {
|
|
55
|
+
pending.clear()
|
|
56
|
+
results.clear()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrofetch
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.SharedPreferences
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
8
|
+
import com.margelo.nitro.NitroModules
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@DoNotStrip
|
|
13
|
+
class NativeStorage : HybridNativeStorageSpec() {
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
private const val TAG = "HybridNativeStorage"
|
|
17
|
+
private const val PREFS_NAME = "nitro_fetch_storage"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
private val sharedPreferences: SharedPreferences by lazy {
|
|
21
|
+
val context = NitroModules.applicationContext ?: throw Error("Cannot get Android Context - No Context available!")
|
|
22
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Retrieves a string value for the given key.
|
|
30
|
+
*
|
|
31
|
+
* @param key The key to look up in storage
|
|
32
|
+
* @return The stored string value, or empty string if key doesn't exist
|
|
33
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
34
|
+
*/
|
|
35
|
+
override fun getString(key: String): String {
|
|
36
|
+
return try {
|
|
37
|
+
val value = sharedPreferences.getString(key, null)
|
|
38
|
+
if (value != null) {
|
|
39
|
+
Log.d(TAG, "Retrieved value for key: $key")
|
|
40
|
+
value
|
|
41
|
+
} else {
|
|
42
|
+
Log.d(TAG, "Key not found: $key, returning empty string")
|
|
43
|
+
""
|
|
44
|
+
}
|
|
45
|
+
} catch (t: Throwable) {
|
|
46
|
+
Log.e(TAG, "Error getting string for key: $key", t)
|
|
47
|
+
throw RuntimeException("Failed to get string for key: $key", t)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stores a string value with the given key.
|
|
53
|
+
*
|
|
54
|
+
* @param key The key to store the value under
|
|
55
|
+
* @param value The string value to store
|
|
56
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
57
|
+
* @throws RuntimeException if the write operation fails
|
|
58
|
+
*/
|
|
59
|
+
override fun setString(key: String, value: String) {
|
|
60
|
+
try {
|
|
61
|
+
val editor = sharedPreferences.edit()
|
|
62
|
+
editor.putString(key, value)
|
|
63
|
+
val success = editor.commit() // commit() is synchronous and returns boolean
|
|
64
|
+
if (success) {
|
|
65
|
+
Log.d(TAG, "Successfully stored value for key: $key")
|
|
66
|
+
} else {
|
|
67
|
+
Log.e(TAG, "Failed to commit value for key: $key")
|
|
68
|
+
throw RuntimeException("Failed to store value for key: $key")
|
|
69
|
+
}
|
|
70
|
+
} catch (t: Throwable) {
|
|
71
|
+
Log.e(TAG, "Error setting string for key: $key", t)
|
|
72
|
+
throw RuntimeException("Failed to set string for key: $key", t)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Deletes the value associated with the given key.
|
|
78
|
+
* If the key doesn't exist, this is a no-op.
|
|
79
|
+
*
|
|
80
|
+
* @param key The key to delete from storage
|
|
81
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
82
|
+
* @throws RuntimeException if the delete operation fails
|
|
83
|
+
*/
|
|
84
|
+
override fun removeString(key: String) {
|
|
85
|
+
try {
|
|
86
|
+
val editor = sharedPreferences.edit()
|
|
87
|
+
editor.remove(key)
|
|
88
|
+
val success = editor.commit() // commit() is synchronous and returns boolean
|
|
89
|
+
if (success) {
|
|
90
|
+
Log.d(TAG, "Successfully deleted key: $key")
|
|
91
|
+
} else {
|
|
92
|
+
Log.e(TAG, "Failed to commit deletion for key: $key")
|
|
93
|
+
throw RuntimeException("Failed to delete key: $key")
|
|
94
|
+
}
|
|
95
|
+
} catch (t: Throwable) {
|
|
96
|
+
Log.e(TAG, "Error deleting key: $key", t)
|
|
97
|
+
throw RuntimeException("Failed to delete key: $key", t)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrofetch
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
|
+
import org.chromium.net.CronetEngine
|
|
7
|
+
import org.chromium.net.CronetProvider
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.util.concurrent.Executor
|
|
10
|
+
import java.util.concurrent.Executors
|
|
11
|
+
|
|
12
|
+
@DoNotStrip
|
|
13
|
+
class NitroFetch : HybridNitroFetchSpec() {
|
|
14
|
+
// Generated base may expect env-less createClient.
|
|
15
|
+
override fun createClient(): NitroFetchClient {
|
|
16
|
+
return NitroFetchClient(getEngine(), ioExecutor)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
companion object {
|
|
20
|
+
@Volatile private var engineRef: CronetEngine? = null
|
|
21
|
+
|
|
22
|
+
// Simpler & safer for callbacks than a pool (avoid reentrancy races in glue code).
|
|
23
|
+
val ioExecutor: Executor by lazy {
|
|
24
|
+
val cores = Runtime.getRuntime().availableProcessors().coerceAtLeast(2)
|
|
25
|
+
Executors.newFixedThreadPool(cores) { r ->
|
|
26
|
+
Thread(r, "NitroCronet-io").apply {
|
|
27
|
+
isDaemon = true
|
|
28
|
+
priority = Thread.NORM_PRIORITY
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fun getEngine(): CronetEngine {
|
|
34
|
+
engineRef?.let { return it }
|
|
35
|
+
synchronized(this) {
|
|
36
|
+
engineRef?.let { return it }
|
|
37
|
+
|
|
38
|
+
val app = currentApplication() ?: initialApplication()
|
|
39
|
+
?: throw IllegalStateException("NitroFetch: Application not available")
|
|
40
|
+
|
|
41
|
+
// Log available providers and prefer the Native one (avoids Play-Services DNS quirks)
|
|
42
|
+
val providers = CronetProvider.getAllProviders(app)
|
|
43
|
+
providers.forEach { Log.i("NitroFetch", "Cronet provider: ${it.name} v=${it.version}") }
|
|
44
|
+
val nativeProvider = providers.firstOrNull { it.name.contains("Native", ignoreCase = true) }
|
|
45
|
+
|
|
46
|
+
val cacheDir = File(app.cacheDir, "nitrofetch_cronet_cache").apply { mkdirs() }
|
|
47
|
+
val builder = (nativeProvider?.createBuilder() ?: CronetEngine.Builder(app))
|
|
48
|
+
.enableHttp2(true)
|
|
49
|
+
.enableQuic(true)
|
|
50
|
+
.enableBrotli(true)
|
|
51
|
+
.setStoragePath(cacheDir.absolutePath)
|
|
52
|
+
.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 10 * 1024 * 1024)
|
|
53
|
+
.setUserAgent("NitroFetch/0.1")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// --- Optional debugging knobs (uncomment temporarily) ---
|
|
57
|
+
// Enable NetLog-like tracing in NetworkService:
|
|
58
|
+
// builder.setExperimentalOptions("""{"NetworkService":{"enable_network_logging":true}}""")
|
|
59
|
+
//
|
|
60
|
+
// Prove DNS issues by mapping a host (TESTING ONLY, remove in prod):
|
|
61
|
+
// builder.setExperimentalOptions("""{"HostResolverRules":{"host_resolver_rules":"MAP httpbin.org 54.167.17.38"}}""")
|
|
62
|
+
|
|
63
|
+
val engine = builder.build()
|
|
64
|
+
Log.i("NitroFetch", "CronetEngine initialized. Provider=${nativeProvider?.name ?: "Default"} Cache=${cacheDir.absolutePath}")
|
|
65
|
+
engineRef = engine
|
|
66
|
+
return engine
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun shutdown() {
|
|
71
|
+
synchronized(this) {
|
|
72
|
+
try {
|
|
73
|
+
engineRef?.shutdown()
|
|
74
|
+
} catch (_: Throwable) {
|
|
75
|
+
// ignore – shutdown is best-effort
|
|
76
|
+
} finally {
|
|
77
|
+
engineRef = null
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private fun currentApplication(): Application? = try {
|
|
83
|
+
val cls = Class.forName("android.app.ActivityThread")
|
|
84
|
+
val m = cls.getMethod("currentApplication")
|
|
85
|
+
m.invoke(null) as? Application
|
|
86
|
+
} catch (_: Throwable) { null }
|
|
87
|
+
|
|
88
|
+
private fun initialApplication(): Application? = try {
|
|
89
|
+
val cls = Class.forName("android.app.AppGlobals")
|
|
90
|
+
val m = cls.getMethod("getInitialApplication")
|
|
91
|
+
m.invoke(null) as? Application
|
|
92
|
+
} catch (_: Throwable) { null }
|
|
93
|
+
}
|
|
94
|
+
}
|