summaryrefslogtreecommitdiffstats
path: root/google_apis
diff options
context:
space:
mode:
authorfgorski <fgorski@chromium.org>2015-10-28 15:48:46 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-28 22:49:30 +0000
commit44f2b96cbe3b2168af54e3fa4c7464f3607474e6 (patch)
tree995e02e5b964a3296ca29b522476e159215c927f /google_apis
parent3568d9cebe7b59f2b24a50690a90469e4afd79f6 (diff)
downloadchromium_src-44f2b96cbe3b2168af54e3fa4c7464f3607474e6.zip
chromium_src-44f2b96cbe3b2168af54e3fa4c7464f3607474e6.tar.gz
chromium_src-44f2b96cbe3b2168af54e3fa4c7464f3607474e6.tar.bz2
[GCM] Adding handling of the immediate ack to Chrome
If a message carries immediate_ack bit, stream ack will be sent immediately upon receipt. This patch also adds a test for that behavior. BUG=548766 Review URL: https://codereview.chromium.org/1408403006 Cr-Commit-Position: refs/heads/master@{#356679}
Diffstat (limited to 'google_apis')
-rw-r--r--google_apis/gcm/base/mcs_util.cc8
-rw-r--r--google_apis/gcm/base/mcs_util.h4
-rw-r--r--google_apis/gcm/engine/mcs_client.cc5
-rw-r--r--google_apis/gcm/engine/mcs_client_unittest.cc264
-rw-r--r--google_apis/gcm/protocol/mcs.proto16
5 files changed, 164 insertions, 133 deletions
diff --git a/google_apis/gcm/base/mcs_util.cc b/google_apis/gcm/base/mcs_util.cc
index 7dd9d7b..b5fb928 100644
--- a/google_apis/gcm/base/mcs_util.cc
+++ b/google_apis/gcm/base/mcs_util.cc
@@ -259,4 +259,12 @@ int GetTTL(const google::protobuf::MessageLite& protobuf) {
return data_message->ttl();
}
+bool IsImmediateAckRequested(const google::protobuf::MessageLite& protobuf) {
+ if (protobuf.GetTypeName() != kProtoNames[kDataMessageStanzaTag])
+ return false;
+ const mcs_proto::DataMessageStanza* data_message =
+ reinterpret_cast<const mcs_proto::DataMessageStanza*>(&protobuf);
+ return data_message->has_immediate_ack() && data_message->immediate_ack();
+}
+
} // namespace gcm
diff --git a/google_apis/gcm/base/mcs_util.h b/google_apis/gcm/base/mcs_util.h
index 95d97f2..e6fc1e0 100644
--- a/google_apis/gcm/base/mcs_util.h
+++ b/google_apis/gcm/base/mcs_util.h
@@ -89,6 +89,10 @@ GCM_EXPORT bool HasTTLExpired(const google::protobuf::MessageLite& protobuf,
base::Clock* clock);
GCM_EXPORT int GetTTL(const google::protobuf::MessageLite& protobuf);
+// Returns whether the message was sent with an immediate ack request.
+GCM_EXPORT bool IsImmediateAckRequested(
+ const google::protobuf::MessageLite& protobuf);
+
} // namespace gcm
#endif // GOOGLE_APIS_GCM_BASE_MCS_UTIL_H_
diff --git a/google_apis/gcm/engine/mcs_client.cc b/google_apis/gcm/engine/mcs_client.cc
index f20d937..2e52ddb 100644
--- a/google_apis/gcm/engine/mcs_client.cc
+++ b/google_apis/gcm/engine/mcs_client.cc
@@ -681,8 +681,9 @@ void MCSClient::HandlePacketFromWire(
<< ", stream id " << stream_id_in_ << " and last stream id received "
<< last_stream_id_received;
- if (unacked_server_ids_.size() > 0 &&
- unacked_server_ids_.size() % kUnackedMessageBeforeStreamAck == 0) {
+ if ((unacked_server_ids_.size() > 0 &&
+ unacked_server_ids_.size() % kUnackedMessageBeforeStreamAck == 0) ||
+ IsImmediateAckRequested(*protobuf)) {
SendMessage(MCSMessage(kIqStanzaTag, BuildStreamAck()));
}
diff --git a/google_apis/gcm/engine/mcs_client_unittest.cc b/google_apis/gcm/engine/mcs_client_unittest.cc
index 6c7ea72..961c36d 100644
--- a/google_apis/gcm/engine/mcs_client_unittest.cc
+++ b/google_apis/gcm/engine/mcs_client_unittest.cc
@@ -41,6 +41,13 @@ const int kAckLimitSize = 10;
// TTL value for reliable messages.
const int kTTLValue = 5 * 60; // 5 minutes.
+// Specifies whether immediate ACK should be requested.
+enum RequestImmediateAck {
+ IMMEDIATE_ACK_IGNORE, // Ignores the field and does not set it.
+ IMMEDIATE_ACK_NO, // Sets the field to false.
+ IMMEDIATE_ACK_YES // Sets the field to true.
+};
+
// Helper for building arbitrary data messages.
MCSMessage BuildDataMessage(const std::string& from,
const std::string& category,
@@ -51,7 +58,8 @@ MCSMessage BuildDataMessage(const std::string& from,
uint64 sent,
int queued,
const std::string& token,
- const uint64& user_id) {
+ const uint64& user_id,
+ RequestImmediateAck immediate_ack) {
mcs_proto::DataMessageStanza data_message;
data_message.set_id(message_id);
data_message.set_from(from);
@@ -64,6 +72,9 @@ MCSMessage BuildDataMessage(const std::string& from,
data_message.set_queued(queued);
data_message.set_token(token);
data_message.set_device_user_id(user_id);
+ if (immediate_ack != IMMEDIATE_ACK_IGNORE) {
+ data_message.set_immediate_ack(immediate_ack == IMMEDIATE_ACK_YES);
+ }
return MCSMessage(kDataMessageStanzaTag, data_message);
}
@@ -342,8 +353,8 @@ TEST_F(MCSClientTest, SendMessageNoRMQ) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
- MCSMessage message(
- BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0,
+ "", 0, IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
@@ -356,8 +367,8 @@ TEST_F(MCSClientTest, SendMessageNoRMQWhileDisconnected) {
InitializeClient();
EXPECT_TRUE(sent_message_id().empty());
- MCSMessage message(
- BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0,
+ "", 0, IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message);
// Message sent callback should be invoked, but no message should actually
@@ -372,8 +383,9 @@ TEST_F(MCSClientTest, SendMessageRMQ) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
@@ -386,8 +398,9 @@ TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
// The initial (failed) send.
GetFakeHandler()->ExpectOutgoingMessage(message);
@@ -395,8 +408,9 @@ TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
GetFakeHandler()->ExpectOutgoingMessage(MCSMessage(
kLoginRequestTag, BuildLoginRequest(kAndroidId, kSecurityToken, "")));
// The second (re)send.
- MCSMessage message2(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
+ MCSMessage message2(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, kTTLValue - 1, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message2);
mcs_client()->SendMessage(message);
PumpLoop(); // Wait for the queuing to happen.
@@ -427,8 +441,9 @@ TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
// The initial (failed) send.
GetFakeHandler()->ExpectOutgoingMessage(message);
@@ -443,8 +458,9 @@ TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
InitializeClient();
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
- MCSMessage message2(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
+ MCSMessage message2(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, kTTLValue - 1, "", 0,
+ IMMEDIATE_ACK_NO));
LoginClient(std::vector<std::string>());
GetFakeHandler()->ExpectOutgoingMessage(message2);
PumpLoop();
@@ -460,16 +476,9 @@ TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
// Send some messages.
for (int i = 1; i <= kMessageBatchSize; ++i) {
- MCSMessage message(BuildDataMessage("from",
- "category",
- "X",
- 1,
- base::IntToString(i),
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1,
+ base::IntToString(i), kTTLValue, 1, 0,
+ "", 0, IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
@@ -501,16 +510,9 @@ TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
- MCSMessage message(BuildDataMessage("from",
- "category",
- id_list.back(),
- 1,
- id_list.back(),
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", id_list.back(), 1,
+ id_list.back(), kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
@@ -540,16 +542,9 @@ TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
- MCSMessage message(BuildDataMessage("from",
- "category",
- id_list.back(),
- 1,
- id_list.back(),
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", id_list.back(), 1,
+ id_list.back(), kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
@@ -571,16 +566,9 @@ TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
id_list.begin() + kMessageBatchSize / 2,
id_list.end());
for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
- MCSMessage message(BuildDataMessage("from",
- "category",
- remaining_ids[i - 1],
- 2,
- remaining_ids[i - 1],
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage(
+ "from", "category", remaining_ids[i - 1], 2, remaining_ids[i - 1],
+ kTTLValue, 1, 0, "", 0, IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
}
scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
@@ -600,16 +588,18 @@ TEST_F(MCSClientTest, SelectiveAckMidStream) {
// Server stream id 2 ("s1").
// Acks client stream id 0 (login).
- MCSMessage sMessage1(BuildDataMessage(
- "from", "category", "X", 0, "s1", kTTLValue, 1, 0, "", 0));
+ MCSMessage sMessage1(BuildDataMessage("from", "category", "X", 0, "s1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ReceiveMessage(sMessage1);
WaitForMCSEvent();
PumpLoop();
// Client stream id 1 ("1").
// Acks server stream id 2 ("s1").
- MCSMessage cMessage1(BuildDataMessage(
- "from", "category", "Y", 2, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage cMessage1(BuildDataMessage("from", "category", "Y", 2, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(cMessage1);
mcs_client()->SendMessage(cMessage1);
PumpLoop();
@@ -618,16 +608,18 @@ TEST_F(MCSClientTest, SelectiveAckMidStream) {
// Server stream id 3 ("s2").
// Acks client stream id 1 ("1").
// Confirms ack of server stream id 2 ("s1").
- MCSMessage sMessage2(BuildDataMessage(
- "from", "category", "X", 1, "s2", kTTLValue, 1, 0, "", 0));
+ MCSMessage sMessage2(BuildDataMessage("from", "category", "X", 1, "s2",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ReceiveMessage(sMessage2);
WaitForMCSEvent();
PumpLoop();
// Client Stream id 2 ("2").
// Acks server stream id 3 ("s2").
- MCSMessage cMessage2(BuildDataMessage(
- "from", "category", "Y", 3, "2", kTTLValue, 1, 0, "", 0));
+ MCSMessage cMessage2(BuildDataMessage("from", "category", "Y", 3, "2",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(cMessage2);
mcs_client()->SendMessage(cMessage2);
PumpLoop();
@@ -637,8 +629,9 @@ TEST_F(MCSClientTest, SelectiveAckMidStream) {
// ack client message "1".
// Client message "2" should be resent, acking server stream id 4 (selective
// ack).
- MCSMessage cMessage3(BuildDataMessage(
- "from", "category", "Y", 4, "2", kTTLValue, 1, 0, "", 0));
+ MCSMessage cMessage3(BuildDataMessage("from", "category", "Y", 4, "2",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(cMessage3);
std::vector<std::string> acked_ids(1, "1");
scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
@@ -658,8 +651,9 @@ TEST_F(MCSClientTest, SelectiveAckMidStream) {
acked_ids[0] = "s2";
LoginClient(acked_ids);
- MCSMessage cMessage4(BuildDataMessage(
- "from", "category", "Y", 1, "2", kTTLValue, 1, 0, "", 0));
+ MCSMessage cMessage4(BuildDataMessage("from", "category", "Y", 1, "2",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(cMessage4);
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
@@ -676,8 +670,9 @@ TEST_F(MCSClientTest, AckOnLogin) {
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1,
+ id_list.back(), kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ReceiveMessage(message);
WaitForMCSEvent();
PumpLoop();
@@ -701,31 +696,17 @@ TEST_F(MCSClientTest, AckOnSend) {
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
- MCSMessage message(BuildDataMessage("from",
- "category",
- id_list.back(),
- 1,
- id_list.back(),
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", id_list.back(), 1,
+ id_list.back(), kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ReceiveMessage(message);
PumpLoop();
}
// Trigger a message send, which should acknowledge via stream ack.
- MCSMessage message(BuildDataMessage("from",
- "category",
- "X",
- kMessageBatchSize + 1,
- "1",
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X",
+ kMessageBatchSize + 1, "1", kTTLValue, 1,
+ 0, "", 0, IMMEDIATE_ACK_NO));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
@@ -747,16 +728,9 @@ TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
std::vector<std::string> id_list;
for (int i = 1; i <= kAckLimitSize; ++i) {
id_list.push_back(base::IntToString(i));
- MCSMessage message(BuildDataMessage("from",
- "category",
- id_list.back(),
- 1,
- id_list.back(),
- kTTLValue,
- 1,
- 0,
- "",
- 0));
+ MCSMessage message(BuildDataMessage("from", "category", id_list.back(), 1,
+ id_list.back(), kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
GetFakeHandler()->ReceiveMessage(message);
WaitForMCSEvent();
PumpLoop();
@@ -793,8 +767,9 @@ TEST_F(MCSClientTest, ExpiredTTLOnSend) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
// Advance time to after the TTL.
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
@@ -812,8 +787,9 @@ TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
- MCSMessage message(BuildDataMessage(
- "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
+ MCSMessage message(BuildDataMessage("from", "category", "X", 1, "1",
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
// The initial (failed) send.
GetFakeHandler()->ExpectOutgoingMessage(message);
@@ -840,12 +816,14 @@ TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
TEST_F(MCSClientTest, CollapseKeysSameApp) {
BuildMCSClient();
InitializeClient();
- MCSMessage message(BuildDataMessage(
- "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
+ MCSMessage message(BuildDataMessage("from", "app", "message id 1", 1, "1",
+ kTTLValue, 1, 0, "token", 0,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message);
- MCSMessage message2(BuildDataMessage(
- "from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0));
+ MCSMessage message2(BuildDataMessage("from", "app", "message id 2", 1, "1",
+ kTTLValue, 1, 0, "token", 0,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
@@ -858,20 +836,14 @@ TEST_F(MCSClientTest, CollapseKeysSameApp) {
TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
BuildMCSClient();
InitializeClient();
- MCSMessage message(BuildDataMessage(
- "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
+ MCSMessage message(BuildDataMessage("from", "app", "message id 1", 1, "1",
+ kTTLValue, 1, 0, "token", 0,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message);
- MCSMessage message2(BuildDataMessage("from",
- "app 2",
- "message id 2",
- 1,
- "2",
- kTTLValue,
- 1,
- 0,
- "token",
- 0));
+ MCSMessage message2(BuildDataMessage("from", "app 2", "message id 2", 1, "2",
+ kTTLValue, 1, 0, "token", 0,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
@@ -885,20 +857,14 @@ TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
TEST_F(MCSClientTest, CollapseKeysDifferentUser) {
BuildMCSClient();
InitializeClient();
- MCSMessage message(BuildDataMessage(
- "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
+ MCSMessage message(BuildDataMessage("from", "app", "message id 1", 1, "1",
+ kTTLValue, 1, 0, "token", 0,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message);
- MCSMessage message2(BuildDataMessage("from",
- "app",
- "message id 2",
- 1,
- "2",
- kTTLValue,
- 1,
- 0,
- "token",
- 1));
+ MCSMessage message2(BuildDataMessage("from", "app", "message id 2", 1, "2",
+ kTTLValue, 1, 0, "token", 1,
+ IMMEDIATE_ACK_NO));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
@@ -1175,6 +1141,42 @@ TEST_F(MCSClientTest, CustomHeartbeatIntervalRemoveShorterInterval) {
EXPECT_EQ(interval_ms, hb_manager->GetClientHeartbeatIntervalMs());
}
+// Receive a message with immediate ack request, which should trigger an
+// automatic stream ack.
+TEST_F(MCSClientTest, AckWhenImmediateAckRequested) {
+ BuildMCSClient();
+ InitializeClient();
+ LoginClient(std::vector<std::string>());
+
+ // The stream ack.
+ scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
+ ack->set_last_stream_id_received(kAckLimitSize - 1);
+ GetFakeHandler()->ExpectOutgoingMessage(MCSMessage(kIqStanzaTag, ack.Pass()));
+
+ // Receive some messages.
+ for (int i = 1; i < kAckLimitSize - 2; ++i) {
+ std::string id(base::IntToString(i));
+ MCSMessage message(BuildDataMessage("from", "category", id, 1, id,
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_NO));
+ GetFakeHandler()->ReceiveMessage(message);
+ WaitForMCSEvent();
+ PumpLoop();
+ }
+ // This message expects immediate ACK, which means it will happen before the
+ // ACK limit size is reached. All of the preceding messages will be acked at
+ // the same time.
+ std::string ack_id(base::IntToString(kAckLimitSize - 1));
+ MCSMessage message(BuildDataMessage("from", "category", ack_id, 1, ack_id,
+ kTTLValue, 1, 0, "", 0,
+ IMMEDIATE_ACK_YES));
+ GetFakeHandler()->ReceiveMessage(message);
+ WaitForMCSEvent();
+ PumpLoop();
+
+ EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
+}
+
} // namespace
} // namespace gcm
diff --git a/google_apis/gcm/protocol/mcs.proto b/google_apis/gcm/protocol/mcs.proto
index a1ea614..cba5845 100644
--- a/google_apis/gcm/protocol/mcs.proto
+++ b/google_apis/gcm/protocol/mcs.proto
@@ -253,6 +253,22 @@ message DataMessageStanza {
// Optional field containing the binary payload of the message.
optional bytes raw_data = 21;
+
+ // Not used.
+ // The maximum delay of the message, in seconds.
+ // optional int32 max_delay = 22;
+
+ // Not used.
+ // How long the message was delayed before it was sent, in seconds.
+ // optional int32 actual_delay = 23;
+
+ // If set the server requests immediate ack. Used for important messages and
+ // for testing.
+ optional bool immediate_ack = 24;
+
+ // Not used.
+ // Enables message receipts from MCS/GCM back to CCS clients
+ // optional bool delivery_receipt_requested = 25;
}
/**