Преглед на файлове

Skip unknown fields while decoding BatchGetDocumentsResponse proto objects (#1377)

rsgowman преди 7 години
родител
ревизия
9307f48930
променени са 2 файла, в които са добавени 68 реда и са изтрити 8 реда
  1. 6 8
      Firestore/core/src/firebase/firestore/remote/serializer.cc
  2. 62 0
      Firestore/core/test/firebase/firestore/remote/serializer_test.cc

+ 6 - 8
Firestore/core/src/firebase/firestore/remote/serializer.cc

@@ -551,9 +551,10 @@ std::unique_ptr<MaybeDocument> Serializer::DecodeBatchGetDocumentsResponse(
         break;
 
       default:
-        reader->set_status(Status(
-            FirestoreErrorCode::DataLoss,
-            "Input proto bytes cannot be parsed (invalid field number (tag))"));
+        // Unknown tag. According to the proto spec, we need to ignore these. No
+        // action required here, though we'll need to skip the relevant bytes
+        // below.
+        break;
     }
 
     if (!reader->status().ok()) return nullptr;
@@ -595,11 +596,8 @@ std::unique_ptr<MaybeDocument> Serializer::DecodeBatchGetDocumentsResponse(
         break;
 
       default:
-        // This indicates an internal error as we've already ensured that this
-        // is a valid field_number.
-        HARD_FAIL(
-            "Somehow got an unexpected field number (tag) after verifying that "
-            "the field number was expected.");
+        // Unknown tag. According to the proto spec, we need to ignore these.
+        reader->SkipField(tag);
     }
   }
 

+ 62 - 0
Firestore/core/test/firebase/firestore/remote/serializer_test.cc

@@ -804,6 +804,68 @@ TEST_F(SerializerTest, EncodesNonEmptyDocument) {
   ExpectRoundTrip(key, fields, update_time, proto);
 }
 
+TEST_F(SerializerTest,
+       BadBatchGetDocumentsResponseTagWithOtherValidTagsPresent) {
+  // A bad tag should be ignored, in which case, we should successfully
+  // deserialize the rest of the bytes as if it wasn't there. To craft these
+  // bytes, we'll use the same technique as
+  // EncodesFieldValuesWithRepeatedEntries (so go read the comments there
+  // first).
+
+  // Copy of the real one (from the nanopb generated firestore.pb.h), but with
+  // only "missing" (a field from the original proto) and an extra_field.
+  struct google_firestore_v1beta1_BatchGetDocumentsResponse_Fake {
+    pb_callback_t missing;
+    int64_t extra_field;
+  };
+
+  // Copy of the real one (from the nanopb generated firestore.pb.c), but with
+  // only missing and an extra_field. Also modified such that extra_field
+  // now has a tag of 31.
+  const int invalid_tag = 31;
+  const pb_field_t
+      google_firestore_v1beta1_BatchGetDocumentsResponse_fields_Fake[2] = {
+          PB_FIELD(2, STRING, SINGULAR, CALLBACK, FIRST,
+                   google_firestore_v1beta1_BatchGetDocumentsResponse_Fake,
+                   missing, missing, 0),
+          PB_FIELD(invalid_tag, INT64, SINGULAR, STATIC, OTHER,
+                   google_firestore_v1beta1_BatchGetDocumentsResponse_Fake,
+                   extra_field, missing, 0),
+      };
+
+  const char* missing_value = "projects/p/databases/d/documents/one/two";
+  google_firestore_v1beta1_BatchGetDocumentsResponse_Fake crafty_value;
+  crafty_value.missing.funcs.encode =
+      [](pb_ostream_t* stream, const pb_field_t* field, void* const* arg) {
+        const char* missing_value = static_cast<const char*>(*arg);
+        if (!pb_encode_tag_for_field(stream, field)) return false;
+        return pb_encode_string(stream,
+                                reinterpret_cast<const uint8_t*>(missing_value),
+                                strlen(missing_value));
+      };
+  crafty_value.missing.arg = const_cast<char*>(missing_value);
+  crafty_value.extra_field = 42;
+
+  std::vector<uint8_t> bytes(128);
+  pb_ostream_t stream = pb_ostream_from_buffer(bytes.data(), bytes.size());
+  pb_encode(&stream,
+            google_firestore_v1beta1_BatchGetDocumentsResponse_fields_Fake,
+            &crafty_value);
+  bytes.resize(stream.bytes_written);
+
+  // Decode the bytes into the model
+  StatusOr<std::unique_ptr<MaybeDocument>> actual_model_status =
+      serializer.DecodeMaybeDocument(bytes);
+  EXPECT_OK(actual_model_status);
+  std::unique_ptr<MaybeDocument> actual_model =
+      std::move(actual_model_status).ValueOrDie();
+
+  // Ensure the decoded model is as expected.
+  NoDocument expected_model =
+      NoDocument(Key("one/two"), SnapshotVersion::None());
+  EXPECT_EQ(expected_model, *actual_model.get());
+}
+
 TEST_F(SerializerTest, DecodesNoDocument) {
   // We can't actually *encode* a NoDocument; the method exposed by the
   // serializer requires both the document key and contents (as an ObjectValue,