Skip to content

Commit 3389e11

Browse files
committed
fix: encoding support + N 9 support
1 parent 4340c90 commit 3389e11

6 files changed

Lines changed: 102 additions & 33 deletions

File tree

packages/https/platforms/android/java/com/nativescript/https/OkHttpResponse.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.io.InputStream;
99
import java.io.File;
1010
import java.io.IOException;
11+
import java.nio.charset.Charset;
12+
import java.nio.charset.StandardCharsets;
1113

1214
import android.graphics.Bitmap;
1315
import android.graphics.BitmapFactory;
@@ -409,24 +411,41 @@ public void run() {
409411
thread.start();
410412
}
411413

412-
static String responseBodyToString(OkHttpResponse response) throws IOException {
413-
414-
final String responseString = response.responseBody.string();
415-
response.closeResponseBody();
416-
return responseString;
414+
static String responseBodyToString(OkHttpResponse response, String encoding) throws IOException {
415+
if (encoding == null) {
416+
final String responseString = response.responseBody.string();
417+
response.closeResponseBody();
418+
return responseString;
419+
}
420+
byte[] bytes = response.responseBody.bytes(); // consumes the body
421+
Charset charset;
422+
switch (encoding) {
423+
case "GBK":
424+
charset = Charset.forName("GBK");
425+
break;
426+
case "ASCII":
427+
charset = StandardCharsets.US_ASCII;
428+
break;
429+
default:
430+
charset = StandardCharsets.UTF_8;
431+
}
432+
433+
String responseString = new String(bytes, charset);
434+
response.closeResponseBody();
435+
return responseString;
417436
}
418437

419-
public String asString() throws IOException {
420-
return responseBodyToString(this);
438+
public String asString(String encoding) throws IOException {
439+
return responseBodyToString(this, encoding);
421440
}
422441

423-
public void asStringAsync(final OkHttpResponseAsyncCallback callback) {
442+
public void asStringAsync(String encoding, final OkHttpResponseAsyncCallback callback) {
424443
final OkHttpResponse fme = this;
425444
Thread thread = new Thread(new Runnable() {
426445
@Override
427446
public void run() {
428447
try {
429-
final String result = responseBodyToString(fme);
448+
final String result = responseBodyToString(fme, encoding);
430449
if (runOnMainThread) {
431450
getMainHandler().post(new Runnable() {
432451
@Override

src/https/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { ImageSource } from '@nativescript/core';
22
import type { HttpsRequestOptions } from './request';
3-
// eslint-disable-next-line no-duplicate-imports
43
import { request } from './request';
54
export * from './request';
65

6+
// for shim to work with N 9
7+
export { request as requestInternal };
8+
79
/**
810
* Downloads the content from the specified URL as a string.
911
* @param arg either The URL to request from or HttpsRequestOptions options.

src/https/request.android.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { File, HttpResponseEncoding, ImageSource, Utils } from '@nativescript/core';
1+
import { File, ImageSource } from '@nativescript/core';
22
import { CacheOptions, HttpsFormDataParam, HttpsRequest, HttpsRequestOptions, HttpsSSLPinningOptions, HttpsResponseLegacy as IHttpsResponseLegacy } from '.';
33

4-
import { getFilenameFromUrl, interceptors, networkInterceptors, parseJSON } from './request.common';
5-
export { addNetworkInterceptor, addInterceptor } from './request.common';
4+
import { HttpResponseEncoding, getFilenameFromUrl, interceptors, networkInterceptors, parseJSON } from './request.common';
5+
export { HttpResponseEncoding, addInterceptor, addNetworkInterceptor } from './request.common';
66

77
interface Ipeer {
88
enabled: boolean;
@@ -123,36 +123,36 @@ class HttpsResponseLegacy implements IHttpsResponseLegacy {
123123

124124
// cache it because asking it again wont work as the socket is closed
125125
stringResponse: string;
126-
toString(encoding?: string) {
126+
toString(encoding?: HttpResponseEncoding) {
127127
// TODO: handle arraybuffer already stored
128-
this.stringResponse = this.stringResponse || this.response.asString();
128+
this.stringResponse = this.stringResponse || this.response.asString(encoding);
129129
return this.stringResponse;
130130
}
131-
async toStringAsync(encoding?: string): Promise<string> {
131+
async toStringAsync(encoding?: HttpResponseEncoding): Promise<string> {
132132
if (this.stringResponse) {
133133
return this.stringResponse;
134134
}
135135
// TODO: handle arraybuffer already stored
136136
this.stringResponse = await new Promise<string>((resolve, reject) => {
137137
this.getOrCreateCloseCallback();
138-
this.response.asStringAsync(this.getCallback(resolve, reject));
138+
this.response.asStringAsync(encoding, this.getCallback(resolve, reject));
139139
});
140140
return this.stringResponse;
141141
}
142142

143143
// cache it because asking it again wont work as the socket is closed
144144
jsonResponse: any;
145-
toJSON(encoding?: string) {
145+
toJSON(encoding?: HttpResponseEncoding) {
146146
if (this.jsonResponse !== undefined) {
147147
return this.jsonResponse;
148148
}
149149
// TODO: handle arraybuffer already stored
150-
this.stringResponse = this.stringResponse || this.response.asString();
150+
this.stringResponse = this.stringResponse || this.response.asString(encoding);
151151
this.jsonResponse = this.stringResponse ? parseJSON(this.stringResponse) : null;
152152
return this.jsonResponse;
153153
}
154154

155-
async toJSONAsync<T>() {
155+
async toJSONAsync<T>(encoding?: HttpResponseEncoding) {
156156
if (this.jsonResponse !== undefined) {
157157
return this.jsonResponse;
158158
}
@@ -161,7 +161,7 @@ class HttpsResponseLegacy implements IHttpsResponseLegacy {
161161
return this.jsonResponse;
162162
}
163163
// TODO: handle arraybuffer already stored
164-
const r = await this.toStringAsync();
164+
const r = await this.toStringAsync(encoding);
165165
this.jsonResponse = r ? parseJSON(r) : null;
166166
return this.jsonResponse as T;
167167
}
@@ -248,6 +248,19 @@ const SDKVersion = android.os.Build.VERSION.SDK_INT;
248248
let Client: okhttp3.OkHttpClient;
249249
let cookieJar: com.nativescript.https.QuotePreservingCookieJar;
250250
let cookieManager: java.net.CookieManager;
251+
252+
export function getCookie(key: string) {
253+
const cookies = cookieManager?.getCookieStore()?.getCookies();
254+
if (cookies) {
255+
for (let index = 0; index < cookies.size(); index++) {
256+
const cookie = cookies.get(index) as java.net.HttpCookie;
257+
const name = cookie.getName();
258+
if (name === key) {
259+
return cookie.getValue();
260+
}
261+
}
262+
}
263+
}
251264
export function getClient(opts: Partial<HttpsRequestOptions> = {}, reload: boolean = false): okhttp3.OkHttpClient {
252265
if (!Client) {
253266
// ssl error fix on KitKat. Only need to be done once.

src/https/request.common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { knownFolders, path } from '@nativescript/core';
22

3+
export enum HttpResponseEncoding {
4+
ASCII = 'ASCII',
5+
UTF8 = 'UTF-8',
6+
GBK = 'GBK'
7+
}
8+
39
export function getFilenameFromUrl(url: string) {
410
const slashPos = url.lastIndexOf('/') + 1;
511
const questionMarkPos = url.lastIndexOf('?');

src/https/request.ios.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { File, ImageSource, Utils } from '@nativescript/core';
22
import { CacheOptions, HttpsFormDataParam, HttpsRequest, HttpsRequestOptions, HttpsResponse, HttpsSSLPinningOptions, HttpsResponseLegacy as IHttpsResponseLegacy } from '.';
3-
import { getFilenameFromUrl, parseJSON } from './request.common';
4-
export { addInterceptor, addNetworkInterceptor } from './request.common';
3+
import { getFilenameFromUrl, HttpResponseEncoding, parseJSON } from './request.common';
4+
export { HttpResponseEncoding, addInterceptor, addNetworkInterceptor } from './request.common';
55

66
let cache: NSURLCache;
77

@@ -75,9 +75,23 @@ function nativeToObj(data, encoding?) {
7575
});
7676
return content;
7777
} else if (data instanceof NSData) {
78-
return NSString.alloc()
79-
.initWithDataEncoding(data, encoding === 'ascii' ? NSASCIIStringEncoding : NSUTF8StringEncoding)
80-
.toString();
78+
let code = NSUTF8StringEncoding; // long:4
79+
80+
if (encoding === HttpResponseEncoding.GBK) {
81+
code = CFStringEncodings.kCFStringEncodingGB_18030_2000; // long:1586
82+
} else if (encoding === HttpResponseEncoding.ASCII) {
83+
code = NSASCIIStringEncoding;
84+
}
85+
86+
let encodedString = NSString.alloc().initWithDataEncoding(data, code);
87+
88+
// If UTF8 string encoding fails try with ISO-8859-1
89+
if (!encodedString) {
90+
code = NSISOLatin1StringEncoding; // long:5
91+
encodedString = NSString.alloc().initWithDataEncoding(data, code);
92+
}
93+
94+
return encodedString.toString();
8195
} else {
8296
return data;
8397
}
@@ -138,7 +152,7 @@ class HttpsResponseLegacy implements IHttpsResponseLegacy {
138152
return this.arrayBuffer;
139153
}
140154
stringResponse: string;
141-
toString(encoding?: any) {
155+
toString(encoding?: HttpResponseEncoding) {
142156
if (!this.data) {
143157
return null;
144158
}
@@ -163,11 +177,11 @@ class HttpsResponseLegacy implements IHttpsResponseLegacy {
163177
return this.stringResponse;
164178
}
165179
}
166-
toStringAsync(encoding?: any) {
180+
toStringAsync(encoding?: HttpResponseEncoding) {
167181
return Promise.resolve(this.toString(encoding));
168182
}
169183
jsonResponse: any;
170-
toJSON<T>(encoding?: any) {
184+
toJSON<T>(encoding?: HttpResponseEncoding) {
171185
if (!this.data) {
172186
return null;
173187
}
@@ -187,8 +201,8 @@ class HttpsResponseLegacy implements IHttpsResponseLegacy {
187201
this.jsonResponse = data ? parseJSON(data) : null;
188202
return this.jsonResponse as T;
189203
}
190-
toJSONAsync<T>() {
191-
return Promise.resolve<T>(this.toJSON());
204+
toJSONAsync<T>(encoding?: HttpResponseEncoding) {
205+
return Promise.resolve<T>(this.toJSON(encoding));
192206
}
193207
imageSource: ImageSource;
194208
async toImage(): Promise<ImageSource> {
@@ -341,6 +355,21 @@ export function clearCookies() {
341355
storage.deleteCookie(cookie);
342356
});
343357
}
358+
export function getCookie(key: string) {
359+
const storage = NSHTTPCookieStorage.sharedHTTPCookieStorage;
360+
const cookies = storage.cookies;
361+
if (cookies) {
362+
let result;
363+
// TODO: stop when found
364+
cookies.enumerateObjectsUsingBlock((cookie: NSHTTPCookie, index, stop: interop.Pointer | interop.Reference<boolean>) => {
365+
storage.deleteCookie(cookie);
366+
if (cookie.name === key) {
367+
result = cookie.value;
368+
}
369+
});
370+
return result;
371+
}
372+
}
344373
export function createRequest(opts: HttpsRequestOptions, useLegacy: boolean = true): HttpsRequest {
345374
const type = opts.headers && opts.headers['Content-Type'] ? opts.headers['Content-Type'] : 'application/json';
346375
if (type.startsWith('application/json')) {

src/https/typings/android.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ declare namespace com {
2020
cancel();
2121
toByteArray();
2222
toByteArrayAsync(callback: OkHttpResponse.OkHttpResponseAsyncCallback);
23-
asString();
24-
asStringAsync(callback: OkHttpResponse.OkHttpResponseAsyncCallback);
23+
asString(encoding: string);
24+
asStringAsync(encoding: string, callback: OkHttpResponse.OkHttpResponseAsyncCallback);
2525
toImage();
2626
toImageAsync(callback: OkHttpResponse.OkHttpResponseAsyncCallback);
2727
toFile();

0 commit comments

Comments
 (0)