Skip to content

Commit 236f2da

Browse files
authored
Android: Allow to provide a different HttpClient implementation (#1067)
* Android: Allow to provide a different HttpClient implementation * Remove Map of headers
1 parent a0a5fe1 commit 236f2da

File tree

5 files changed

+139
-39
lines changed

5 files changed

+139
-39
lines changed

lib/android_build/app/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ android {
3030
version "3.10.2"
3131
}
3232
}
33+
compileOptions {
34+
sourceCompatibility JavaVersion.VERSION_1_8
35+
targetCompatibility JavaVersion.VERSION_1_8
36+
}
3337
}
3438

3539
dependencies {

lib/android_build/app/src/test/java/com/microsoft/applications/events/maesdktest/ExampleUnitTest.java

Lines changed: 0 additions & 21 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
package com.microsoft.applications.events.maesdktest;
6+
7+
import com.microsoft.applications.events.HttpClientRequest;
8+
9+
import org.junit.Assert;
10+
import org.junit.Test;
11+
12+
import java.nio.charset.Charset;
13+
import java.util.ArrayList;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
import static java.nio.charset.StandardCharsets.UTF_8;
18+
import static org.junit.Assert.*;
19+
20+
public class HttpClientRequestTest {
21+
@Test
22+
public void testHeaders() {
23+
byte[] bytes = "key1value1key33key2value2".getBytes(UTF_8);
24+
int[] header_lengths = new int[] { 4, 6, 5, 0, 4, 6 };
25+
26+
HttpClientRequest.Headers headers = new HttpClientRequest.Headers(header_lengths, bytes);
27+
ArrayList<String> actual = new ArrayList<>();
28+
while (headers.hasNext()) {
29+
HttpClientRequest.HeaderEntry entry = headers.next();
30+
actual.add(entry.key);
31+
actual.add(entry.value);
32+
}
33+
34+
Assert.assertEquals(6, headers.length);
35+
Assert.assertArrayEquals(
36+
new String[] {"key1", "value1", "key33", "", "key2", "value2"},
37+
actual.toArray()
38+
);
39+
}
40+
}

lib/android_build/maesdk/src/main/java/com/microsoft/applications/events/HttpClient.java

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@
2323
import android.os.Build;
2424
import android.provider.Settings;
2525
import android.util.Log;
26+
2627
import androidx.annotation.Keep;
2728
import androidx.annotation.NonNull;
2829
import androidx.annotation.Nullable;
2930
import androidx.annotation.RequiresApi;
31+
3032
import java.io.BufferedInputStream;
3133
import java.io.OutputStream;
3234
import java.net.HttpURLConnection;
3335
import java.net.URL;
3436
import java.text.SimpleDateFormat;
3537
import java.util.Calendar;
3638
import java.util.Date;
39+
import java.util.Iterator;
3740
import java.util.List;
3841
import java.util.Locale;
3942
import java.util.Map;
@@ -86,16 +89,15 @@ public final void onCapabilitiesChanged(
8689
private boolean m_metered;
8790
}
8891

89-
class Request implements Runnable {
92+
class Request implements HttpClientRequest {
9093

9194
Request(
92-
HttpClient parent,
93-
String url,
94-
String method,
95-
byte[] body,
96-
String request_id,
97-
int[] header_length,
98-
byte[] header_buffer)
95+
@NonNull HttpClient parent,
96+
String url,
97+
String method,
98+
byte[] body,
99+
String request_id,
100+
@NonNull Headers headers)
99101
throws java.io.IOException {
100102
m_parent = parent;
101103
m_connection = (HttpURLConnection) parent.newUrl(url).openConnection();
@@ -106,13 +108,9 @@ class Request implements Runnable {
106108
m_connection.setDoOutput(true);
107109
}
108110
m_request_id = request_id;
109-
int offset = 0;
110-
for (int i = 0; i + 1 < header_length.length; i += 2) {
111-
String k = new String(header_buffer, offset, header_length[i], UTF_8);
112-
offset += header_length[i];
113-
String v = new String(header_buffer, offset, header_length[i + 1], UTF_8);
114-
offset += header_length[i + 1];
115-
m_connection.setRequestProperty(k, v);
111+
while (headers.hasNext()) {
112+
HeaderEntry header = headers.next();
113+
m_connection.setRequestProperty(header.key, header.value);
116114
}
117115
}
118116

@@ -190,7 +188,7 @@ public class HttpClient {
190188

191189
/** Shim FutureTask: we would like to @Keep the cancel method for JNI */
192190
static class FutureShim extends FutureTask<Boolean> {
193-
FutureShim(Request inner) {
191+
FutureShim(HttpClientRequest inner) {
194192
super(inner, true);
195193
}
196194

@@ -202,7 +200,12 @@ public boolean cancel(boolean mayInterruptIfRunning) {
202200
}
203201

204202
public HttpClient(Context context) {
203+
this(context, new HttpClientRequest.Factory.AndroidUrlConnection());
204+
}
205+
206+
public HttpClient(Context context, HttpClientRequest.Factory requestFactory) {
205207
m_context = context;
208+
m_requestFactory = requestFactory;
206209
String path = System.getProperty("java.io.tmpdir");
207210
setCacheFilePath(path);
208211
setDeviceInfo(calculateID(context), Build.MANUFACTURER, Build.MODEL);
@@ -376,7 +379,8 @@ public FutureTask<Boolean> createTask(
376379
int[] header_index,
377380
byte[] header_buffer) {
378381
try {
379-
Request r = new Request(this, url, method, body, request_id, header_index, header_buffer);
382+
HttpClientRequest.Headers headers = new HttpClientRequest.Headers(header_index, header_buffer);
383+
HttpClientRequest r = m_requestFactory.create(this, url, method, body, request_id, headers);
380384
return new FutureShim(r);
381385
} catch (Exception e) {
382386
return null;
@@ -393,4 +397,5 @@ public void executeTask(FutureTask<Boolean> t) {
393397
private android.net.ConnectivityManager m_connectivityManager;
394398
private PowerInfoReceiver m_power_receiver;
395399
private final Context m_context;
396-
}
400+
private final HttpClientRequest.Factory m_requestFactory;
401+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
package com.microsoft.applications.events;
6+
7+
import androidx.annotation.NonNull;
8+
9+
import java.io.IOException;
10+
import java.util.Iterator;
11+
12+
import static java.nio.charset.StandardCharsets.UTF_8;
13+
14+
public interface HttpClientRequest extends Runnable {
15+
16+
interface Factory {
17+
HttpClientRequest create(
18+
@NonNull HttpClient parent,
19+
String url,
20+
String method,
21+
byte[] body,
22+
String request_id,
23+
@NonNull Headers headers
24+
) throws IOException;
25+
26+
class AndroidUrlConnection implements HttpClientRequest.Factory {
27+
@Override
28+
public HttpClientRequest create(@NonNull HttpClient parent, String url, String method, byte[] body, String request_id, @NonNull Headers headers) throws IOException {
29+
return new Request(parent, url, method, body, request_id, headers);
30+
}
31+
}
32+
}
33+
34+
final class HeaderEntry {
35+
public final String key;
36+
public final String value;
37+
38+
public HeaderEntry(String key, String value) {
39+
this.key = key;
40+
this.value = value;
41+
}
42+
}
43+
44+
final class Headers implements Iterator<HeaderEntry> {
45+
public final int length;
46+
private final int[] header_lengths;
47+
private final byte[] buffer;
48+
private int current = 0;
49+
private int offset = 0;
50+
51+
public Headers(int[] header_lengths, byte[] buffer) {
52+
this.length = header_lengths.length;
53+
this.header_lengths = header_lengths;
54+
this.buffer = buffer;
55+
}
56+
57+
@Override
58+
public boolean hasNext() {
59+
return current + 2 <= length;
60+
}
61+
62+
@Override
63+
public HeaderEntry next() {
64+
String k = new String(buffer, offset, header_lengths[current], UTF_8);
65+
offset += header_lengths[current];
66+
String v = new String(buffer, offset, header_lengths[current + 1], UTF_8);
67+
offset += header_lengths[current + 1];
68+
current += 2;
69+
return new HeaderEntry(k,v);
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)