one 2.0.0-beta.106.1 → 2.0.0-beta.107.1
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/android/src/main/java/com/margelo/nitro/one/HybridOneFetch.kt +250 -0
- package/dist/cjs/platform/fetch/index.cjs +24 -0
- package/dist/cjs/platform/fetch/index.native.cjs +434 -0
- package/dist/cjs/platform/fetch/index.native.js +436 -0
- package/dist/cjs/platform/fetch/index.native.js.map +1 -0
- package/dist/cjs/platform/specs/OneFetch.nitro.cjs +16 -0
- package/dist/cjs/platform/specs/OneFetch.nitro.native.cjs +18 -0
- package/dist/cjs/platform/specs/OneFetch.nitro.native.js +20 -0
- package/dist/cjs/platform/specs/OneFetch.nitro.native.js.map +1 -0
- package/dist/cjs/polyfills-mobile.cjs +2 -0
- package/dist/cjs/polyfills-mobile.native.cjs +2 -0
- package/dist/cjs/polyfills-mobile.native.js +2 -0
- package/dist/cjs/polyfills-mobile.native.js.map +1 -1
- package/dist/esm/platform/fetch/index.mjs +4 -0
- package/dist/esm/platform/fetch/index.mjs.map +1 -0
- package/dist/esm/platform/fetch/index.native.js +407 -0
- package/dist/esm/platform/fetch/index.native.js.map +1 -0
- package/dist/esm/platform/specs/OneFetch.nitro.mjs +0 -0
- package/dist/esm/platform/specs/OneFetch.nitro.native.js +0 -0
- package/dist/esm/polyfills-mobile.mjs +2 -0
- package/dist/esm/polyfills-mobile.mjs.map +1 -1
- package/dist/esm/polyfills-mobile.native.js +2 -0
- package/dist/esm/polyfills-mobile.native.js.map +1 -1
- package/ios/Nitro/HybridOneFetch.swift +264 -0
- package/ios/OneFetchBlobStoreModule.mm +28 -0
- package/nitrogen/generated/android/One+autolinking.cmake +2 -0
- package/nitrogen/generated/android/OneOnLoad.cpp +22 -0
- package/nitrogen/generated/android/c++/JFetchBlobRef.hpp +65 -0
- package/nitrogen/generated/android/c++/JFetchFormPart.hpp +80 -0
- package/nitrogen/generated/android/c++/JFetchHeader.hpp +61 -0
- package/nitrogen/generated/android/c++/JFetchNativeRequest.hpp +131 -0
- package/nitrogen/generated/android/c++/JFetchNativeResponse.hpp +94 -0
- package/nitrogen/generated/android/c++/JFunc_void_FetchNativeResponse.hpp +81 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__shared_ptr_ArrayBuffer_.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridOneFetchSpec.cpp +90 -0
- package/nitrogen/generated/android/c++/JHybridOneFetchSpec.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/FetchBlobRef.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/FetchFormPart.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/FetchHeader.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/FetchNativeRequest.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/FetchNativeResponse.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/Func_void_FetchNativeResponse.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/Func_void_std__shared_ptr_ArrayBuffer_.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/Func_void_std__string.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/one/HybridOneFetchSpec.kt +70 -0
- package/nitrogen/generated/ios/One-Swift-Cxx-Bridge.cpp +33 -0
- package/nitrogen/generated/ios/One-Swift-Cxx-Bridge.hpp +152 -0
- package/nitrogen/generated/ios/One-Swift-Cxx-Umbrella.hpp +20 -0
- package/nitrogen/generated/ios/OneAutolinking.mm +8 -0
- package/nitrogen/generated/ios/OneAutolinking.swift +12 -0
- package/nitrogen/generated/ios/c++/HybridOneFetchSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridOneFetchSpecSwift.hpp +115 -0
- package/nitrogen/generated/ios/swift/FetchBlobRef.swift +39 -0
- package/nitrogen/generated/ios/swift/FetchFormPart.swift +112 -0
- package/nitrogen/generated/ios/swift/FetchHeader.swift +34 -0
- package/nitrogen/generated/ios/swift/FetchNativeRequest.swift +121 -0
- package/nitrogen/generated/ios/swift/FetchNativeResponse.swift +55 -0
- package/nitrogen/generated/ios/swift/Func_void_FetchNativeResponse.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridOneFetchSpec.swift +57 -0
- package/nitrogen/generated/ios/swift/HybridOneFetchSpec_cxx.swift +180 -0
- package/nitrogen/generated/shared/c++/FetchBlobRef.hpp +91 -0
- package/nitrogen/generated/shared/c++/FetchFormPart.hpp +106 -0
- package/nitrogen/generated/shared/c++/FetchHeader.hpp +87 -0
- package/nitrogen/generated/shared/c++/FetchNativeRequest.hpp +122 -0
- package/nitrogen/generated/shared/c++/FetchNativeResponse.hpp +102 -0
- package/nitrogen/generated/shared/c++/HybridOneFetchSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridOneFetchSpec.hpp +71 -0
- package/package.json +10 -10
- package/src/platform/README.md +38 -0
- package/src/platform/fetch/index.native.ts +345 -0
- package/src/platform/fetch/index.ts +2 -0
- package/src/platform/specs/OneFetch.nitro.ts +67 -0
- package/src/polyfills-mobile.ts +9 -0
- package/types/platform/fetch/index.d.ts +2 -0
- package/types/platform/fetch/index.d.ts.map +1 -0
- package/types/platform/fetch/index.native.d.ts +2 -0
- package/types/platform/fetch/index.native.d.ts.map +1 -0
- package/types/platform/specs/OneFetch.nitro.d.ts +44 -0
- package/types/platform/specs/OneFetch.nitro.d.ts.map +1 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
package com.margelo.nitro.one
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import android.webkit.MimeTypeMap
|
|
5
|
+
import com.facebook.react.modules.blob.BlobModule
|
|
6
|
+
import com.facebook.react.modules.network.CookieJarContainer
|
|
7
|
+
import com.facebook.react.modules.network.ForwardingCookieHandler
|
|
8
|
+
import com.facebook.react.modules.network.OkHttpClientProvider
|
|
9
|
+
import com.margelo.nitro.NitroModules
|
|
10
|
+
import com.margelo.nitro.core.ArrayBuffer
|
|
11
|
+
import java.io.IOException
|
|
12
|
+
import java.io.InputStream
|
|
13
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
14
|
+
import java.util.concurrent.Executors
|
|
15
|
+
import okhttp3.Call
|
|
16
|
+
import okhttp3.Callback
|
|
17
|
+
import okhttp3.OkHttpClient
|
|
18
|
+
import okhttp3.CookieJar
|
|
19
|
+
import okhttp3.Headers
|
|
20
|
+
import okhttp3.JavaNetCookieJar
|
|
21
|
+
import okhttp3.MediaType
|
|
22
|
+
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
23
|
+
import okhttp3.MultipartBody
|
|
24
|
+
import okhttp3.Request
|
|
25
|
+
import okhttp3.RequestBody
|
|
26
|
+
import okhttp3.RequestBody.Companion.toRequestBody
|
|
27
|
+
import okhttp3.Response
|
|
28
|
+
import okhttp3.internal.http.HttpMethod
|
|
29
|
+
import okio.BufferedSink
|
|
30
|
+
|
|
31
|
+
// the global fetch on native, over an OkHttp client built the way react
|
|
32
|
+
// native's NetworkingModule builds its own: from OkHttpClientProvider, so an
|
|
33
|
+
// app's OkHttpClientFactory (interceptors, certificate pinning) applies, with
|
|
34
|
+
// its cookie jar backed by the same CookieManager store react native uses. the response head goes to js first, then every chunk as the
|
|
35
|
+
// socket delivers it, so response.body streams. file: and content: urls are
|
|
36
|
+
// read from disk, as react native's fetch reads them.
|
|
37
|
+
class HybridOneFetch : HybridOneFetchSpec() {
|
|
38
|
+
// cancel actions for live requests; whoever removes an id owns its
|
|
39
|
+
// terminal event, so a cancelled request never reports
|
|
40
|
+
private val active = ConcurrentHashMap<Double, () -> Unit>()
|
|
41
|
+
private val local = Executors.newCachedThreadPool()
|
|
42
|
+
|
|
43
|
+
private val client: OkHttpClient by lazy {
|
|
44
|
+
OkHttpClientProvider.createClient().also {
|
|
45
|
+
(it.cookieJar as? CookieJarContainer)
|
|
46
|
+
?.setCookieJar(JavaNetCookieJar(ForwardingCookieHandler()))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
private val clientWithoutCookies: OkHttpClient by lazy {
|
|
50
|
+
client.newBuilder().cookieJar(CookieJar.NO_COOKIES).build()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private val context
|
|
54
|
+
get() = NitroModules.applicationContext
|
|
55
|
+
?: throw IllegalStateException("fetch: the react context is not ready")
|
|
56
|
+
|
|
57
|
+
override fun start(
|
|
58
|
+
id: Double,
|
|
59
|
+
request: FetchNativeRequest,
|
|
60
|
+
onResponse: (response: FetchNativeResponse) -> Unit,
|
|
61
|
+
onChunk: (chunk: ArrayBuffer) -> Unit,
|
|
62
|
+
onComplete: () -> Unit,
|
|
63
|
+
onError: (message: String) -> Unit
|
|
64
|
+
) {
|
|
65
|
+
val scheme = Uri.parse(request.url).scheme
|
|
66
|
+
if (scheme == "file" || scheme == "content") {
|
|
67
|
+
readLocal(id, request.url, onResponse, onChunk, onComplete, onError)
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
val built = try {
|
|
71
|
+
val builder = Request.Builder().url(request.url)
|
|
72
|
+
for (header in request.headers) builder.addHeader(header.name, header.value)
|
|
73
|
+
val type = request.headers
|
|
74
|
+
.lastOrNull { it.name.equals("content-type", ignoreCase = true) }
|
|
75
|
+
?.value?.toMediaTypeOrNull()
|
|
76
|
+
builder.method(request.method, body(request, type))
|
|
77
|
+
builder.build()
|
|
78
|
+
} catch (error: IllegalArgumentException) {
|
|
79
|
+
onError(error.message ?: "invalid request")
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
val call = (if (request.omitCredentials) clientWithoutCookies else client).newCall(built)
|
|
83
|
+
active[id] = { call.cancel() }
|
|
84
|
+
call.enqueue(object : Callback {
|
|
85
|
+
override fun onFailure(call: Call, e: IOException) {
|
|
86
|
+
if (active.remove(id) != null) onError(e.message ?: e.javaClass.simpleName)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun onResponse(call: Call, response: Response) {
|
|
90
|
+
response.use {
|
|
91
|
+
if (!active.containsKey(id)) return
|
|
92
|
+
val headers = response.headers.map { (name, value) -> FetchHeader(name, value) }
|
|
93
|
+
onResponse(
|
|
94
|
+
FetchNativeResponse(
|
|
95
|
+
response.code.toDouble(),
|
|
96
|
+
response.message,
|
|
97
|
+
response.request.url.toString(),
|
|
98
|
+
response.priorResponse != null,
|
|
99
|
+
headers.toTypedArray()
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
pump(id, response.body?.byteStream(), onChunk, onComplete, onError)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override fun cancel(id: Double) {
|
|
109
|
+
active.remove(id)?.invoke()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun storeBlob(bytes: ArrayBuffer): String = blobModule().store(bytes.toByteArray())
|
|
113
|
+
|
|
114
|
+
private fun blobModule(): BlobModule =
|
|
115
|
+
context.getNativeModule(BlobModule::class.java)
|
|
116
|
+
?: throw IllegalStateException("fetch: react native's BlobModule is not loaded")
|
|
117
|
+
|
|
118
|
+
// the request body. js-owned bytes are copied now, during the call;
|
|
119
|
+
// blobs, files and multipart parts are read on the network thread. every
|
|
120
|
+
// length is known up front where react native's was, so uploads carry a
|
|
121
|
+
// content-length rather than going out chunked.
|
|
122
|
+
private fun body(request: FetchNativeRequest, type: MediaType?): RequestBody? {
|
|
123
|
+
request.body?.let { return it.toByteArray().toRequestBody(type) }
|
|
124
|
+
request.blob?.let { return blobBody(it, type) }
|
|
125
|
+
val form = request.form
|
|
126
|
+
val boundary = request.boundary
|
|
127
|
+
if (form != null && boundary != null) return multipart(form, boundary)
|
|
128
|
+
return if (HttpMethod.requiresRequestBody(request.method)) ByteArray(0).toRequestBody(type)
|
|
129
|
+
else null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private fun blobBody(blob: FetchBlobRef, type: MediaType?) = object : RequestBody() {
|
|
133
|
+
override fun contentType() = type
|
|
134
|
+
override fun contentLength() = blob.size.toLong()
|
|
135
|
+
override fun writeTo(sink: BufferedSink) {
|
|
136
|
+
sink.write(resolve(blob))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private fun uriBody(uri: String, type: MediaType?) = object : RequestBody() {
|
|
141
|
+
override fun contentType() = type
|
|
142
|
+
override fun contentLength() =
|
|
143
|
+
context.contentResolver.openAssetFileDescriptor(Uri.parse(uri), "r")?.use { it.length }
|
|
144
|
+
?: -1L
|
|
145
|
+
override fun writeTo(sink: BufferedSink) {
|
|
146
|
+
open(uri).use { input -> input.copyTo(sink.outputStream()) }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private fun resolve(blob: FetchBlobRef): ByteArray =
|
|
151
|
+
blobModule().resolve(blob.blobId, blob.offset.toInt(), blob.size.toInt())
|
|
152
|
+
?: throw IOException("fetch: blob ${blob.blobId} is no longer in the store")
|
|
153
|
+
|
|
154
|
+
private fun open(uri: String): InputStream =
|
|
155
|
+
context.contentResolver.openInputStream(Uri.parse(uri))
|
|
156
|
+
?: throw IOException("fetch: could not open $uri")
|
|
157
|
+
|
|
158
|
+
// multipart/form-data per RFC 7578 through okhttp's MultipartBody, as
|
|
159
|
+
// react native builds it, under the boundary js already put in the
|
|
160
|
+
// content-type header
|
|
161
|
+
private fun multipart(parts: Array<FetchFormPart>, boundary: String): RequestBody {
|
|
162
|
+
val builder = MultipartBody.Builder(boundary).setType(MultipartBody.FORM)
|
|
163
|
+
for (part in parts) {
|
|
164
|
+
var disposition = "form-data; name=\"${escape(part.name)}\""
|
|
165
|
+
val value = part.value
|
|
166
|
+
if (value != null) {
|
|
167
|
+
builder.addPart(Headers.headersOf("Content-Disposition", disposition), value.toRequestBody())
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
val filename = part.filename ?: part.uri?.let { Uri.parse(it).lastPathSegment }
|
|
171
|
+
if (filename != null) disposition += "; filename=\"${escape(filename)}\""
|
|
172
|
+
val type = (part.type ?: "application/octet-stream").toMediaTypeOrNull()
|
|
173
|
+
val uri = part.uri
|
|
174
|
+
val blob = part.blob
|
|
175
|
+
val body = when {
|
|
176
|
+
uri != null -> uriBody(uri, type)
|
|
177
|
+
blob != null -> blobBody(blob, type)
|
|
178
|
+
else -> throw IOException("fetch: FormData part ${part.name} has no value")
|
|
179
|
+
}
|
|
180
|
+
builder.addPart(Headers.headersOf("Content-Disposition", disposition), body)
|
|
181
|
+
}
|
|
182
|
+
return builder.build()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private fun escape(value: String) =
|
|
186
|
+
value.replace("\"", "%22").replace("\r", "%0D").replace("\n", "%0A")
|
|
187
|
+
|
|
188
|
+
private fun readLocal(
|
|
189
|
+
id: Double,
|
|
190
|
+
url: String,
|
|
191
|
+
onResponse: (response: FetchNativeResponse) -> Unit,
|
|
192
|
+
onChunk: (chunk: ArrayBuffer) -> Unit,
|
|
193
|
+
onComplete: () -> Unit,
|
|
194
|
+
onError: (message: String) -> Unit
|
|
195
|
+
) {
|
|
196
|
+
active[id] = {}
|
|
197
|
+
local.execute {
|
|
198
|
+
val input = try {
|
|
199
|
+
open(url)
|
|
200
|
+
} catch (error: Exception) {
|
|
201
|
+
if (active.remove(id) != null) onError(error.message ?: "could not open $url")
|
|
202
|
+
return@execute
|
|
203
|
+
}
|
|
204
|
+
input.use {
|
|
205
|
+
val uri = Uri.parse(url)
|
|
206
|
+
val type = context.contentResolver.getType(uri)
|
|
207
|
+
?: MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
|
208
|
+
MimeTypeMap.getFileExtensionFromUrl(url)
|
|
209
|
+
)
|
|
210
|
+
val headers = type?.let { arrayOf(FetchHeader("content-type", it)) } ?: emptyArray()
|
|
211
|
+
if (!active.containsKey(id)) return@execute
|
|
212
|
+
onResponse(FetchNativeResponse(200.0, "", url, false, headers))
|
|
213
|
+
pump(id, input, onChunk, onComplete, onError)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private fun pump(
|
|
219
|
+
id: Double,
|
|
220
|
+
input: InputStream?,
|
|
221
|
+
onChunk: (chunk: ArrayBuffer) -> Unit,
|
|
222
|
+
onComplete: () -> Unit,
|
|
223
|
+
onError: (message: String) -> Unit
|
|
224
|
+
) {
|
|
225
|
+
val buffer = ByteArray(CHUNK)
|
|
226
|
+
try {
|
|
227
|
+
while (input != null) {
|
|
228
|
+
val read = input.read(buffer)
|
|
229
|
+
if (read == -1) break
|
|
230
|
+
if (!active.containsKey(id)) return
|
|
231
|
+
if (read > 0) onChunk(ArrayBuffer.copy(buffer.copyOf(read)))
|
|
232
|
+
}
|
|
233
|
+
} catch (error: IOException) {
|
|
234
|
+
if (active.remove(id) != null) onError(error.message ?: error.javaClass.simpleName)
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
if (active.remove(id) != null) onComplete()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
override fun dispose() {
|
|
241
|
+
active.values.forEach { it() }
|
|
242
|
+
active.clear()
|
|
243
|
+
local.shutdownNow()
|
|
244
|
+
super.dispose()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
companion object {
|
|
248
|
+
private const val CHUNK = 16 * 1024
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
var fetch_exports = {};
|
|
22
|
+
__export(fetch_exports, { installFetch: () => installFetch });
|
|
23
|
+
module.exports = __toCommonJS(fetch_exports);
|
|
24
|
+
function installFetch() {}
|