@capacitor/android 4.1.0 → 4.1.1-dev-7e3589f9.0

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.
@@ -0,0 +1,409 @@
1
+ package com.getcapacitor.plugin.util;
2
+
3
+ import android.text.TextUtils;
4
+ import android.util.Base64;
5
+ import com.getcapacitor.JSArray;
6
+ import com.getcapacitor.JSObject;
7
+ import com.getcapacitor.JSValue;
8
+ import com.getcapacitor.PluginCall;
9
+ import java.io.BufferedReader;
10
+ import java.io.ByteArrayOutputStream;
11
+ import java.io.IOException;
12
+ import java.io.InputStream;
13
+ import java.io.InputStreamReader;
14
+ import java.net.HttpURLConnection;
15
+ import java.net.MalformedURLException;
16
+ import java.net.URI;
17
+ import java.net.URISyntaxException;
18
+ import java.net.URL;
19
+ import java.util.Iterator;
20
+ import java.util.List;
21
+ import java.util.Map;
22
+ import org.json.JSONArray;
23
+ import org.json.JSONException;
24
+ import org.json.JSONObject;
25
+
26
+ public class HttpRequestHandler {
27
+
28
+ /**
29
+ * An enum specifying conventional HTTP Response Types
30
+ * See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
31
+ */
32
+ public enum ResponseType {
33
+ ARRAY_BUFFER("arraybuffer"),
34
+ BLOB("blob"),
35
+ DOCUMENT("document"),
36
+ JSON("json"),
37
+ TEXT("text");
38
+
39
+ private final String name;
40
+
41
+ ResponseType(String name) {
42
+ this.name = name;
43
+ }
44
+
45
+ static final ResponseType DEFAULT = TEXT;
46
+
47
+ static ResponseType parse(String value) {
48
+ for (ResponseType responseType : values()) {
49
+ if (responseType.name.equalsIgnoreCase(value)) {
50
+ return responseType;
51
+ }
52
+ }
53
+ return DEFAULT;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Internal builder class for building a CapacitorHttpUrlConnection
59
+ */
60
+ private static class HttpURLConnectionBuilder {
61
+
62
+ private Integer connectTimeout;
63
+ private Integer readTimeout;
64
+ private Boolean disableRedirects;
65
+ private JSObject headers;
66
+ private String method;
67
+ private URL url;
68
+
69
+ private CapacitorHttpUrlConnection connection;
70
+
71
+ public HttpURLConnectionBuilder setConnectTimeout(Integer connectTimeout) {
72
+ this.connectTimeout = connectTimeout;
73
+ return this;
74
+ }
75
+
76
+ public HttpURLConnectionBuilder setReadTimeout(Integer readTimeout) {
77
+ this.readTimeout = readTimeout;
78
+ return this;
79
+ }
80
+
81
+ public HttpURLConnectionBuilder setDisableRedirects(Boolean disableRedirects) {
82
+ this.disableRedirects = disableRedirects;
83
+ return this;
84
+ }
85
+
86
+ public HttpURLConnectionBuilder setHeaders(JSObject headers) {
87
+ this.headers = headers;
88
+ return this;
89
+ }
90
+
91
+ public HttpURLConnectionBuilder setMethod(String method) {
92
+ this.method = method;
93
+ return this;
94
+ }
95
+
96
+ public HttpURLConnectionBuilder setUrl(URL url) {
97
+ this.url = url;
98
+ return this;
99
+ }
100
+
101
+ public HttpURLConnectionBuilder openConnection() throws IOException {
102
+ connection = new CapacitorHttpUrlConnection((HttpURLConnection) url.openConnection());
103
+
104
+ connection.setAllowUserInteraction(false);
105
+ connection.setRequestMethod(method);
106
+
107
+ if (connectTimeout != null) connection.setConnectTimeout(connectTimeout);
108
+ if (readTimeout != null) connection.setReadTimeout(readTimeout);
109
+ if (disableRedirects != null) connection.setDisableRedirects(disableRedirects);
110
+
111
+ connection.setRequestHeaders(headers);
112
+ return this;
113
+ }
114
+
115
+ public HttpURLConnectionBuilder setUrlParams(JSObject params) throws MalformedURLException, URISyntaxException, JSONException {
116
+ return this.setUrlParams(params, true);
117
+ }
118
+
119
+ public HttpURLConnectionBuilder setUrlParams(JSObject params, boolean shouldEncode)
120
+ throws URISyntaxException, MalformedURLException {
121
+ String initialQuery = url.getQuery();
122
+ String initialQueryBuilderStr = initialQuery == null ? "" : initialQuery;
123
+
124
+ Iterator<String> keys = params.keys();
125
+
126
+ if (!keys.hasNext()) {
127
+ return this;
128
+ }
129
+
130
+ StringBuilder urlQueryBuilder = new StringBuilder(initialQueryBuilderStr);
131
+
132
+ // Build the new query string
133
+ while (keys.hasNext()) {
134
+ String key = keys.next();
135
+
136
+ // Attempt as JSONArray and fallback to string if it fails
137
+ try {
138
+ StringBuilder value = new StringBuilder();
139
+ JSONArray arr = params.getJSONArray(key);
140
+ for (int x = 0; x < arr.length(); x++) {
141
+ value.append(key).append("=").append(arr.getString(x));
142
+ if (x != arr.length() - 1) {
143
+ value.append("&");
144
+ }
145
+ }
146
+ if (urlQueryBuilder.length() > 0) {
147
+ urlQueryBuilder.append("&");
148
+ }
149
+ urlQueryBuilder.append(value);
150
+ } catch (JSONException e) {
151
+ if (urlQueryBuilder.length() > 0) {
152
+ urlQueryBuilder.append("&");
153
+ }
154
+ urlQueryBuilder.append(key).append("=").append(params.getString(key));
155
+ }
156
+ }
157
+
158
+ String urlQuery = urlQueryBuilder.toString();
159
+
160
+ URI uri = url.toURI();
161
+ if (shouldEncode) {
162
+ URI encodedUri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), urlQuery, uri.getFragment());
163
+ this.url = encodedUri.toURL();
164
+ } else {
165
+ String unEncodedUrlString =
166
+ uri.getScheme() +
167
+ "://" +
168
+ uri.getAuthority() +
169
+ uri.getPath() +
170
+ ((!urlQuery.equals("")) ? "?" + urlQuery : "") +
171
+ ((uri.getFragment() != null) ? uri.getFragment() : "");
172
+ this.url = new URL(unEncodedUrlString);
173
+ }
174
+
175
+ return this;
176
+ }
177
+
178
+ public CapacitorHttpUrlConnection build() {
179
+ return connection;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects.
185
+ * Defaults to ResponseType.DEFAULT
186
+ * @param connection The CapacitorHttpUrlConnection to respond with
187
+ * @throws IOException Thrown if the InputStream is unable to be parsed correctly
188
+ * @throws JSONException Thrown if the JSON is unable to be parsed
189
+ */
190
+ private static JSObject buildResponse(CapacitorHttpUrlConnection connection) throws IOException, JSONException {
191
+ return buildResponse(connection, ResponseType.DEFAULT);
192
+ }
193
+
194
+ /**
195
+ * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects
196
+ * @param connection The CapacitorHttpUrlConnection to respond with
197
+ * @param responseType The requested ResponseType
198
+ * @return A JSObject that contains the HTTPResponse to return to the browser
199
+ * @throws IOException Thrown if the InputStream is unable to be parsed correctly
200
+ * @throws JSONException Thrown if the JSON is unable to be parsed
201
+ */
202
+ private static JSObject buildResponse(CapacitorHttpUrlConnection connection, ResponseType responseType)
203
+ throws IOException, JSONException {
204
+ int statusCode = connection.getResponseCode();
205
+
206
+ JSObject output = new JSObject();
207
+ output.put("status", statusCode);
208
+ output.put("headers", buildResponseHeaders(connection));
209
+ output.put("url", connection.getURL());
210
+ output.put("data", readData(connection, responseType));
211
+
212
+ InputStream errorStream = connection.getErrorStream();
213
+ if (errorStream != null) {
214
+ output.put("error", true);
215
+ }
216
+
217
+ return output;
218
+ }
219
+
220
+ /**
221
+ * Read the existing ICapacitorHttpUrlConnection data
222
+ * @param connection The ICapacitorHttpUrlConnection object to read in
223
+ * @param responseType The type of HTTP response to return to the API
224
+ * @return The parsed data from the connection
225
+ * @throws IOException Thrown if the InputStreams cannot be properly parsed
226
+ * @throws JSONException Thrown if the JSON is malformed when parsing as JSON
227
+ */
228
+ static Object readData(ICapacitorHttpUrlConnection connection, ResponseType responseType) throws IOException, JSONException {
229
+ InputStream errorStream = connection.getErrorStream();
230
+ String contentType = connection.getHeaderField("Content-Type");
231
+
232
+ if (errorStream != null) {
233
+ if (isOneOf(contentType, MimeType.APPLICATION_JSON, MimeType.APPLICATION_VND_API_JSON)) {
234
+ return parseJSON(readStreamAsString(errorStream));
235
+ } else {
236
+ return readStreamAsString(errorStream);
237
+ }
238
+ } else if (contentType != null && contentType.contains(MimeType.APPLICATION_JSON.getValue())) {
239
+ // backward compatibility
240
+ return parseJSON(readStreamAsString(connection.getInputStream()));
241
+ } else {
242
+ InputStream stream = connection.getInputStream();
243
+ switch (responseType) {
244
+ case ARRAY_BUFFER:
245
+ case BLOB:
246
+ return readStreamAsBase64(stream);
247
+ case JSON:
248
+ return parseJSON(readStreamAsString(stream));
249
+ case DOCUMENT:
250
+ case TEXT:
251
+ default:
252
+ return readStreamAsString(stream);
253
+ }
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Helper function for determining if the Content-Type is a typeof an existing Mime-Type
259
+ * @param contentType The Content-Type string to check for
260
+ * @param mimeTypes The Mime-Type values to check against
261
+ * @return
262
+ */
263
+ private static boolean isOneOf(String contentType, MimeType... mimeTypes) {
264
+ if (contentType != null) {
265
+ for (MimeType mimeType : mimeTypes) {
266
+ if (contentType.contains(mimeType.getValue())) {
267
+ return true;
268
+ }
269
+ }
270
+ }
271
+ return false;
272
+ }
273
+
274
+ /**
275
+ * Build the JSObject response headers based on the connection header map
276
+ * @param connection The CapacitorHttpUrlConnection connection
277
+ * @return A JSObject of the header values from the CapacitorHttpUrlConnection
278
+ */
279
+ private static JSObject buildResponseHeaders(CapacitorHttpUrlConnection connection) {
280
+ JSObject output = new JSObject();
281
+
282
+ for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
283
+ String valuesString = TextUtils.join(", ", entry.getValue());
284
+ output.put(entry.getKey(), valuesString);
285
+ }
286
+
287
+ return output;
288
+ }
289
+
290
+ /**
291
+ * Returns a JSObject or a JSArray based on a string-ified input
292
+ * @param input String-ified JSON that needs parsing
293
+ * @return A JSObject or JSArray
294
+ * @throws JSONException thrown if the JSON is malformed
295
+ */
296
+ private static Object parseJSON(String input) throws JSONException {
297
+ JSONObject json = new JSONObject();
298
+ try {
299
+ if ("null".equals(input.trim())) {
300
+ return JSONObject.NULL;
301
+ } else if ("true".equals(input.trim())) {
302
+ return new JSONObject().put("flag", "true");
303
+ } else if ("false".equals(input.trim())) {
304
+ return new JSONObject().put("flag", "false");
305
+ } else {
306
+ try {
307
+ return new JSObject(input);
308
+ } catch (JSONException e) {
309
+ return new JSArray(input);
310
+ }
311
+ }
312
+ } catch (JSONException e) {
313
+ return new JSArray(input);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Returns a string based on a base64 InputStream
319
+ * @param in The base64 InputStream to convert to a String
320
+ * @return String value of InputStream
321
+ * @throws IOException thrown if the InputStream is unable to be read as base64
322
+ */
323
+ private static String readStreamAsBase64(InputStream in) throws IOException {
324
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
325
+ byte[] buffer = new byte[1024];
326
+ int readBytes;
327
+ while ((readBytes = in.read(buffer)) != -1) {
328
+ out.write(buffer, 0, readBytes);
329
+ }
330
+ byte[] result = out.toByteArray();
331
+ return Base64.encodeToString(result, 0, result.length, Base64.DEFAULT);
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Returns a string based on an InputStream
337
+ * @param in The InputStream to convert to a String
338
+ * @return String value of InputStream
339
+ * @throws IOException thrown if the InputStream is unable to be read
340
+ */
341
+ private static String readStreamAsString(InputStream in) throws IOException {
342
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
343
+ StringBuilder builder = new StringBuilder();
344
+ String line = reader.readLine();
345
+ while (line != null) {
346
+ builder.append(line);
347
+ line = reader.readLine();
348
+ if (line != null) {
349
+ builder.append(System.getProperty("line.separator"));
350
+ }
351
+ }
352
+ return builder.toString();
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Makes an Http Request based on the PluginCall parameters
358
+ * @param call The Capacitor PluginCall that contains the options need for an Http request
359
+ * @param httpMethod The HTTP method that overrides the PluginCall HTTP method
360
+ * @throws IOException throws an IO request when a connection can't be made
361
+ * @throws URISyntaxException thrown when the URI is malformed
362
+ * @throws JSONException thrown when the incoming JSON is malformed
363
+ */
364
+ public static JSObject request(PluginCall call, String httpMethod) throws IOException, URISyntaxException, JSONException {
365
+ String urlString = call.getString("url", "");
366
+ JSObject headers = call.getObject("headers");
367
+ JSObject params = call.getObject("params");
368
+ Integer connectTimeout = call.getInt("connectTimeout");
369
+ Integer readTimeout = call.getInt("readTimeout");
370
+ Boolean disableRedirects = call.getBoolean("disableRedirects");
371
+ Boolean shouldEncode = call.getBoolean("shouldEncodeUrlParams", true);
372
+ ResponseType responseType = ResponseType.parse(call.getString("responseType"));
373
+
374
+ String method = httpMethod != null ? httpMethod.toUpperCase() : call.getString("method", "").toUpperCase();
375
+
376
+ boolean isHttpMutate = method.equals("DELETE") || method.equals("PATCH") || method.equals("POST") || method.equals("PUT");
377
+
378
+ URL url = new URL(urlString);
379
+ HttpURLConnectionBuilder connectionBuilder = new HttpURLConnectionBuilder()
380
+ .setUrl(url)
381
+ .setMethod(method)
382
+ .setHeaders(headers)
383
+ .setUrlParams(params, shouldEncode)
384
+ .setConnectTimeout(connectTimeout)
385
+ .setReadTimeout(readTimeout)
386
+ .setDisableRedirects(disableRedirects)
387
+ .openConnection();
388
+
389
+ CapacitorHttpUrlConnection connection = connectionBuilder.build();
390
+
391
+ // Set HTTP body on a non GET or HEAD request
392
+ if (isHttpMutate) {
393
+ JSValue data = new JSValue(call, "data");
394
+ if (data.getValue() != null) {
395
+ connection.setDoOutput(true);
396
+ connection.setRequestBody(call, data);
397
+ }
398
+ }
399
+
400
+ connection.connect();
401
+
402
+ return buildResponse(connection, responseType);
403
+ }
404
+
405
+ @FunctionalInterface
406
+ public interface ProgressEmitter {
407
+ void emit(Integer bytes, Integer contentLength);
408
+ }
409
+ }
@@ -0,0 +1,15 @@
1
+ package com.getcapacitor.plugin.util;
2
+
3
+ import java.io.IOException;
4
+ import java.io.InputStream;
5
+
6
+ /**
7
+ * This interface was extracted from {@link CapacitorHttpUrlConnection} to enable mocking that class.
8
+ */
9
+ interface ICapacitorHttpUrlConnection {
10
+ InputStream getErrorStream();
11
+
12
+ String getHeaderField(String name);
13
+
14
+ InputStream getInputStream() throws IOException;
15
+ }
@@ -0,0 +1,17 @@
1
+ package com.getcapacitor.plugin.util;
2
+
3
+ enum MimeType {
4
+ APPLICATION_JSON("application/json"),
5
+ APPLICATION_VND_API_JSON("application/vnd.api+json"), // https://jsonapi.org
6
+ TEXT_HTML("text/html");
7
+
8
+ private final String value;
9
+
10
+ MimeType(String value) {
11
+ this.value = value;
12
+ }
13
+
14
+ String getValue() {
15
+ return value;
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/android",
3
- "version": "4.1.0",
3
+ "version": "4.1.1-dev-7e3589f9.0",
4
4
  "description": "Capacitor: Cross-platform apps with JavaScript and the web",
5
5
  "homepage": "https://capacitorjs.com",
6
6
  "author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
@@ -27,5 +27,6 @@
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
30
- }
30
+ },
31
+ "gitHead": "7e3589f9bb4673b595f2718e444e2bd07c7749e1"
31
32
  }