Skip to content

Commit

Permalink
api: Send simple User-Agent header
Browse files Browse the repository at this point in the history
Fixes: zulip#406
Fixes: zulip#460

[chris: changed commit message; changed user-agent string; included
in image requests]
  • Loading branch information
sirpengi authored and chrisbobbe committed Dec 30, 2023
1 parent bbeae41 commit ac00529
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 4 deletions.
16 changes: 16 additions & 0 deletions lib/api/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class ApiConnection {
assert(debugLog("${request.method} ${request.url}"));

addAuth(request);
request.headers.addAll(userAgentHeader());

final http.StreamedResponse response;
try {
Expand Down Expand Up @@ -198,6 +199,21 @@ Map<String, String> authHeader({required String email, required String apiKey})
};
}

Map<String, String> userAgentHeader() {
return {
// Putting "ZulipMobile" in the string helps users on pre-8 servers:
// - When a user sends a message from the app,
// the message is read instead of unread, avoiding #440.
// (With servers 8+, this is accomplished
// with a new param in the send-message API; see #456.)
// - Login notifications say something specific instead of
// "an unknown browser on an unknown operating system", avoiding #460.
// TODO(server-8) no need to say "ZulipMobile"; simplify and remove comment.
// TODO(#467) include platform, platform version, and app version
'User-Agent': 'ZulipMobile (actually ZulipFlutter)',
};
}

Map<String, String>? encodeParameters(Map<String, dynamic>? params) {
return params?.map((k, v) =>
MapEntry(k, v is RawParameter ? v.value : jsonEncode(v)));
Expand Down
1 change: 1 addition & 0 deletions lib/widgets/content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@ class RealmContentNetworkImage extends StatelessWidget {
if (src.origin == account.realmUrl.origin) ...authHeader(
email: account.email, apiKey: account.apiKey,
),
...userAgentHeader(),
},
cacheWidth: cacheWidth,
cacheHeight: cacheHeight,
Expand Down
12 changes: 10 additions & 2 deletions test/api/core_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ void main() {
check(connection.lastRequest!).isA<http.Request>()
..method.equals('GET')
..url.asString.equals('${eg.realmUrl.origin}$expectedRelativeUrl')
..headers.deepEquals(authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey))
..headers.deepEquals({
...authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey),
...userAgentHeader(),
})
..body.equals('');
});
}
Expand Down Expand Up @@ -53,6 +56,7 @@ void main() {
..url.asString.equals('${eg.realmUrl.origin}/api/v1/example/route')
..headers.deepEquals({
...authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey),
...userAgentHeader(),
if (expectContentType)
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
})
Expand Down Expand Up @@ -83,7 +87,10 @@ void main() {
check(connection.lastRequest!).isA<http.MultipartRequest>()
..method.equals('POST')
..url.asString.equals('${eg.realmUrl.origin}/api/v1/example/route')
..headers.deepEquals(authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey))
..headers.deepEquals({
...authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey),
...userAgentHeader(),
})
..fields.deepEquals({})
..files.single.which(it()
..field.equals('file')
Expand Down Expand Up @@ -115,6 +122,7 @@ void main() {
..url.asString.equals('${eg.realmUrl.origin}/api/v1/example/route')
..headers.deepEquals({
...authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey),
...userAgentHeader(),
if (expectContentType)
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
})
Expand Down
6 changes: 4 additions & 2 deletions test/widgets/content_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,15 @@ void main() {

testWidgets('includes auth header if `src` on-realm', (tester) async {
check(await actualHeaders(tester, Uri.parse('https://chat.example/image.png')))
['Authorization'].single.equals(authHeaders['Authorization']!);
..['Authorization'].single.equals(authHeaders['Authorization']!)
..['User-Agent'].single.equals(userAgentHeader()['User-Agent']!);
debugNetworkImageHttpClientProvider = null;
});

testWidgets('excludes auth header if `src` off-realm', (tester) async {
check(await actualHeaders(tester, Uri.parse('https://other.example/image.png')))
.not(it()..containsKey('Authorization'));
..not(it()..containsKey('Authorization'))
..['User-Agent'].single.equals(userAgentHeader()['User-Agent']!);
debugNetworkImageHttpClientProvider = null;
});

Expand Down

0 comments on commit ac00529

Please sign in to comment.