Skip to content

Commit 9af0079

Browse files
Add User-Agent header to all outbound network requests
1 parent 6b0cfb2 commit 9af0079

File tree

6 files changed

+225
-5
lines changed

6 files changed

+225
-5
lines changed

aws-analytics-pinpoint/src/main/java/com/amplifyframework/analytics/pinpoint/PinpointClientFactory.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import com.amplifyframework.analytics.AnalyticsException;
2121
import com.amplifyframework.core.Amplify;
2222
import com.amplifyframework.logging.Logger;
23+
import com.amplifyframework.util.UserAgent;
2324

25+
import com.amazonaws.ClientConfiguration;
2426
import com.amazonaws.mobile.client.AWSMobileClient;
2527
import com.amazonaws.mobile.client.Callback;
2628
import com.amazonaws.mobile.client.UserStateDetails;
@@ -76,14 +78,17 @@ public void onError(Exception exception) {
7678
throw new RuntimeException("Failed to initialize mobile client: " + exception.getLocalizedMessage());
7779
}
7880

81+
ClientConfiguration clientConfiguration = new ClientConfiguration();
82+
clientConfiguration.setUserAgent(UserAgent.string());
83+
7984
// Construct configuration using information from the configure method
8085
PinpointConfiguration pinpointConfiguration = new PinpointConfiguration(
8186
context,
8287
pinpointAnalyticsPluginConfiguration.getAppId(),
8388
Regions.fromName(pinpointAnalyticsPluginConfiguration.getRegion()),
8489
ChannelType.GCM,
8590
AWSMobileClient.getInstance()
86-
);
91+
).withClientConfiguration(clientConfiguration);
8792

8893
pinpointManager = new PinpointManager(pinpointConfiguration);
8994
return pinpointManager.getAnalyticsClient();

aws-api/src/main/java/com/amplifyframework/api/aws/AWSApiPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.amplifyframework.core.StreamListener;
3939
import com.amplifyframework.core.model.Model;
4040
import com.amplifyframework.core.model.query.predicate.QueryPredicate;
41+
import com.amplifyframework.util.UserAgent;
4142

4243
import org.json.JSONObject;
4344

@@ -108,6 +109,7 @@ public void configure(@NonNull JSONObject pluginConfigurationJson, Context conte
108109
final ApiConfiguration apiConfiguration = entry.getValue();
109110
final EndpointType endpointType = apiConfiguration.getEndpointType();
110111
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
112+
builder.addNetworkInterceptor(UserAgentInterceptor.using(UserAgent::string));
111113
if (apiConfiguration.getAuthorizationType() != AuthorizationType.NONE) {
112114
builder.addInterceptor(interceptorFactory.create(apiConfiguration));
113115
}

aws-api/src/main/java/com/amplifyframework/api/aws/SubscriptionEndpoint.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.amplifyframework.api.graphql.GraphQLRequest;
2626
import com.amplifyframework.api.graphql.GraphQLResponse;
2727
import com.amplifyframework.core.StreamListener;
28+
import com.amplifyframework.util.UserAgent;
2829

2930
import org.json.JSONException;
3031
import org.json.JSONObject;
@@ -128,7 +129,8 @@ private WebSocket createWebSocket() throws ApiException {
128129
.addHeader("Sec-WebSocket-Protocol", "graphql-ws")
129130
.build();
130131

131-
webSocket = new OkHttpClient.Builder()
132+
return new OkHttpClient.Builder()
133+
.addNetworkInterceptor(UserAgentInterceptor.using(UserAgent::string))
132134
.retryOnConnectionFailure(true)
133135
.build()
134136
.newWebSocket(request, new WebSocketListener() {
@@ -153,8 +155,6 @@ public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable failure,
153155
notifyError(failure);
154156
}
155157
});
156-
157-
return webSocket;
158158
}
159159

160160
private void sendConnectionInit(WebSocket webSocket) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.api.aws;
17+
18+
import androidx.annotation.NonNull;
19+
20+
import java.io.IOException;
21+
22+
import okhttp3.Interceptor;
23+
import okhttp3.Request;
24+
import okhttp3.Response;
25+
26+
/**
27+
* An OkHttp3 interceptor which applies a User-Agent header to an outgoing request.
28+
*/
29+
final class UserAgentInterceptor implements Interceptor {
30+
private final UserAgentProvider userAgentProvider;
31+
32+
/**
33+
* Constructs a UserAgentInterceptor.
34+
* @param userAgentProvider A Provider of a user-agent string
35+
*/
36+
private UserAgentInterceptor(final UserAgentProvider userAgentProvider) {
37+
this.userAgentProvider = userAgentProvider;
38+
}
39+
40+
/**
41+
* Creates a user agent interceptor using a user-agent string provider.
42+
* @param userAgentProvider Provider of user-agent string
43+
* @return A UserAgentInterceptor
44+
*/
45+
static UserAgentInterceptor using(UserAgentProvider userAgentProvider) {
46+
return new UserAgentInterceptor(userAgentProvider);
47+
}
48+
49+
@NonNull
50+
@Override
51+
public Response intercept(@NonNull Chain chain) throws IOException {
52+
Request originalRequest = chain.request();
53+
Request requestWithUserAgent = originalRequest.newBuilder()
54+
.header("User-Agent", userAgentProvider.getUserAgent())
55+
.build();
56+
return chain.proceed(requestWithUserAgent);
57+
}
58+
59+
/**
60+
* A provider of a user-agent string.
61+
*/
62+
interface UserAgentProvider {
63+
/**
64+
* Gets the User-Agent string.
65+
* @return User-Agent string
66+
*/
67+
String getUserAgent();
68+
}
69+
}

aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/service/AWSS3StorageService.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import android.content.Intent;
2020

2121
import com.amplifyframework.storage.result.StorageListResult;
22+
import com.amplifyframework.util.UserAgent;
2223

24+
import com.amazonaws.ClientConfiguration;
25+
import com.amazonaws.auth.AWSCredentialsProvider;
2326
import com.amazonaws.mobile.client.AWSMobileClient;
2427
import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
2528
import com.amazonaws.mobileconnectors.s3.transferutility.TransferService;
@@ -59,7 +62,7 @@ public final class AWSS3StorageService {
5962
public AWSS3StorageService(Region region, Context context, String bucket, boolean transferAcceleration) {
6063
this.context = context;
6164
this.bucket = bucket;
62-
this.client = new AmazonS3Client(AWSMobileClient.getInstance(), region);
65+
this.client = createS3Client(region);
6366

6467
if (transferAcceleration) {
6568
client.setS3ClientOptions(S3ClientOptions.builder().setAccelerateModeEnabled(true).build());
@@ -71,6 +74,13 @@ public AWSS3StorageService(Region region, Context context, String bucket, boolea
7174
.build();
7275
}
7376

77+
private AmazonS3Client createS3Client(Region region) {
78+
AWSCredentialsProvider credentialsProvider = AWSMobileClient.getInstance();
79+
ClientConfiguration configuration = new ClientConfiguration();
80+
configuration.setUserAgent(UserAgent.string());
81+
return new AmazonS3Client(credentialsProvider, region, configuration);
82+
}
83+
7484
/**
7585
* Begin downloading a file.
7686
* @param serviceKey S3 service key
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.util;
17+
18+
import android.annotation.SuppressLint;
19+
import androidx.annotation.NonNull;
20+
import androidx.annotation.Nullable;
21+
22+
import com.amazonaws.amplify.core.BuildConfig;
23+
24+
/**
25+
* A utility to construct a User-Agent header, to be sent with all network operations.
26+
*/
27+
public final class UserAgent {
28+
private static String instance = null;
29+
30+
@SuppressWarnings("checkstyle:all") private UserAgent() {}
31+
32+
/**
33+
* Gets a String to use as the value of a User-Agent header.
34+
* @return A value for a User-Agent header.
35+
*/
36+
@SuppressLint("SyntheticAccessor")
37+
@NonNull
38+
public static String string() {
39+
if (instance == null) {
40+
instance = new UserAgent.Builder()
41+
.libraryName("amplify-android")
42+
.libraryVersion(BuildConfig.VERSION_NAME)
43+
.systemName(System.getProperty("os.name"))
44+
.systemVersion(System.getProperty("os.version"))
45+
.javaVmName(System.getProperty("java.vm.name"))
46+
.javaVmVersion(System.getProperty("java.vm.version"))
47+
.javaVersion(System.getProperty("java.version"))
48+
.userLanguage(System.getProperty("user.language"))
49+
.userRegion(System.getProperty("user.region"))
50+
.toString();
51+
}
52+
53+
return instance;
54+
}
55+
56+
@SuppressWarnings("SameParameterValue")
57+
private static final class Builder {
58+
private String libraryName;
59+
private String libraryVersion;
60+
private String systemName;
61+
private String systemVersion;
62+
private String javaVmName;
63+
private String javaVmVersion;
64+
private String javaVersion;
65+
private String userLanguage;
66+
private String userRegion;
67+
68+
Builder libraryName(String libraryName) {
69+
this.libraryName = sanitize(libraryName);
70+
return this;
71+
}
72+
73+
Builder libraryVersion(String libraryVersion) {
74+
this.libraryVersion = sanitize(libraryVersion);
75+
return this;
76+
}
77+
78+
Builder systemName(String systemName) {
79+
this.systemName = sanitize(systemName);
80+
return this;
81+
}
82+
83+
Builder systemVersion(String systemVersion) {
84+
this.systemVersion = sanitize(systemVersion);
85+
return this;
86+
}
87+
88+
Builder javaVmName(String javaVmName) {
89+
this.javaVmName = sanitize(javaVmName);
90+
return this;
91+
}
92+
93+
Builder javaVmVersion(String javaVmVersion) {
94+
this.javaVmVersion = sanitize(javaVmVersion);
95+
return this;
96+
}
97+
98+
Builder javaVersion(String javaVersion) {
99+
this.javaVersion = sanitize(javaVersion);
100+
return this;
101+
}
102+
103+
Builder userLanguage(String userLanguage) {
104+
this.userLanguage = sanitize(userLanguage);
105+
return this;
106+
}
107+
108+
Builder userRegion(String userRegion) {
109+
this.userRegion = sanitize(userRegion);
110+
return this;
111+
}
112+
113+
@NonNull
114+
@Override
115+
public String toString() {
116+
return String.format(
117+
"%s/%s %s/%s %s/%s/%s %s_%s",
118+
libraryName, libraryVersion,
119+
systemName, systemVersion,
120+
javaVmName, javaVmVersion, javaVersion,
121+
userLanguage, userRegion
122+
);
123+
}
124+
125+
@NonNull
126+
private static String sanitize(@Nullable String string) {
127+
if (string == null) {
128+
return "UNKNOWN";
129+
}
130+
131+
return string.replace(' ', '_');
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)