@mentra/crust 3.3.0-dev.382 → 3.3.0-dev.388

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.
@@ -54,6 +54,23 @@ object JSCPolyfillBridge {
54
54
 
55
55
  /** Shared OkHttp execution path for host cloud-client requests. */
56
56
  fun executeHttp(method: String, url: String, headers: Map<String, String>, bodyString: String?): HttpResult {
57
+ val request = buildHttpRequest(method, url, headers, bodyString)
58
+ httpClient.newCall(request).execute().use { response ->
59
+ val responseHeaders = mutableMapOf<String, String>()
60
+ for (name in response.headers.names()) {
61
+ responseHeaders[name.lowercase()] = response.headers.values(name).joinToString(", ")
62
+ }
63
+ return HttpResult(
64
+ status = response.code,
65
+ statusText = response.message,
66
+ headers = responseHeaders,
67
+ body = response.body?.string() ?: "",
68
+ )
69
+ }
70
+ }
71
+
72
+ /** Keep host HTTP and miniapp fetch body semantics identical, including empty POSTs. */
73
+ internal fun buildHttpRequest(method: String, url: String, headers: Map<String, String>, bodyString: String?): Request {
57
74
  val builder = Request.Builder().url(url)
58
75
  for ((name, value) in headers) builder.header(name, value)
59
76
  val contentType = headers.entries
@@ -66,18 +83,7 @@ object JSCPolyfillBridge {
66
83
  else -> null
67
84
  }
68
85
  builder.method(upperMethod, requestBody)
69
- httpClient.newCall(builder.build()).execute().use { response ->
70
- val responseHeaders = mutableMapOf<String, String>()
71
- for (name in response.headers.names()) {
72
- responseHeaders[name.lowercase()] = response.headers.values(name).joinToString(", ")
73
- }
74
- return HttpResult(
75
- status = response.code,
76
- statusText = response.message,
77
- headers = responseHeaders,
78
- body = response.body?.string() ?: "",
79
- )
80
- }
86
+ return builder.build()
81
87
  }
82
88
 
83
89
  /** Idempotent. Call once on host boot, after the dispatcher is created. */
@@ -220,20 +226,9 @@ object JSCPolyfillBridge {
220
226
  }?.toMap() ?: emptyMap()
221
227
  val bodyString = req["body"] as? String
222
228
 
223
- val builder = Request.Builder().url(url)
224
- for ((k, v) in headers) builder.header(k, v)
225
- // HTTP header names are case-insensitive. The JS fetch caller will
226
- // commonly provide `Content-Type`; a direct lowercase map lookup
227
- // misses that value and causes OkHttp to emit application/octet-stream.
228
- val contentType = headers.entries
229
- .firstOrNull { (name, _) -> name.equals("content-type", ignoreCase = true) }
230
- ?.value
231
- val body = if (bodyString.isNullOrEmpty()) null else bodyString.toRequestBody(
232
- (contentType ?: "application/octet-stream").toMediaTypeOrNull()
233
- )
234
- builder.method(method.uppercase(), body)
229
+ val request = buildHttpRequest(method, url, headers, bodyString)
235
230
 
236
- httpClient.newCall(builder.build()).enqueue(object : Callback {
231
+ httpClient.newCall(request).enqueue(object : Callback {
237
232
  override fun onFailure(call: Call, e: IOException) {
238
233
  deliverError(runtime, packageName, reqId, "fetch: ${e.message}")
239
234
  }
@@ -0,0 +1,32 @@
1
+ package com.mentra.crust.jsc
2
+
3
+ import okhttp3.Request
4
+ import okio.Buffer
5
+ import org.junit.Assert.*
6
+ import org.junit.Test
7
+
8
+ class HttpRequestTest {
9
+ private fun request(method: String, body: String? = null): Request =
10
+ JSCPolyfillBridge.buildHttpRequest(method, "https://example.com/stream", mapOf("Content-Type" to "application/json"), body)
11
+
12
+ @Test fun `bodyless post put and patch produce valid zero byte requests`() {
13
+ for (method in listOf("post", "PUT", "PATCH")) for (body in listOf(null, "")) {
14
+ val request = request(method, body)
15
+ assertEquals(method.uppercase(), request.method)
16
+ assertNotNull(request.body)
17
+ assertEquals(0L, request.body!!.contentLength())
18
+ }
19
+ }
20
+
21
+ @Test fun `get head and delete can omit a body`() {
22
+ for (method in listOf("GET", "HEAD", "DELETE")) assertNull(request(method).body)
23
+ }
24
+
25
+ @Test fun `nonempty body and content type survive request construction`() {
26
+ val request = request("POST", "{\"status\":\"stopped\"}")
27
+ val buffer = Buffer()
28
+ request.body!!.writeTo(buffer)
29
+ assertEquals("{\"status\":\"stopped\"}", buffer.readUtf8())
30
+ assertEquals("application/json; charset=utf-8", request.body!!.contentType().toString())
31
+ }
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/crust",
3
- "version": "3.3.0-dev.382",
3
+ "version": "3.3.0-dev.388",
4
4
  "description": "Mentra Native Module",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -35,7 +35,7 @@
35
35
  "license": "Apache-2.0",
36
36
  "homepage": "https://github.com/Mentra-Community/MentraOS/tree/dev/mobile/modules/crust#readme",
37
37
  "dependencies": {
38
- "@mentra/jspolyfill": "3.3.0-dev.382"
38
+ "@mentra/jspolyfill": "3.3.0-dev.388"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@expo/config-plugins": ">=8.0.0",