Repository: googleapis/google-cloud-python

Issue:
Incorrect padding error
Hi,
This is in regards to the pubsub module.  When I send a test message with data "testing 1 2 3" from the web console to a simple python application it fails to decode the message and throws a 

```
binascii.Error: Incorrect padding
```

Error from 

```
  File "/python3.5/site-packages/google/cloud/pubsub/message.py", line 88, in from_api_repr
    data = base64.b64decode(api_repr.get('data', b''))
  File "/lib/python3.5/base64.py", line 88, in b64decode
```

I get the same error if I send the same message using the Go libraries.
I do not get the error if I send the message using python however.
All I'm doing is receiving and printing out the messages.



Discussion:
> When I send a test message with data "testing 1 2 3"

Can you give some code snippets indicating how you do this? Thanks.

I was using the Google Cloud console to do so by clicking on the publish button.  I also did it using the Go library I'll get you the code for that later today.

```
func (q *PubSubClient) PublishTransaction(t *db.TrxModel) error {
    ctx := context.Background()
    data, err := json.Marshal(t)
    if err != nil {
        return err
    }

    _, err = q.client.Topic(q.config.TrxTopic).Publish(ctx, &pubsub.Message{
        Data: data,
    })
    return err
}
```

Here is what the go code looks like, we sent the data "testing 1 2 3" .

I would also add that "testing 1 2 3 4" works fine so I'm guessing its something to do with how other languages pad the base64 encoded bytestring.  Maybe you can do something like this,

http://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding

To check for incorrect padding and add them in as necessary?

@Teddy-Schmitz, this is actually the github repository for the Google Cloud Python library.

In your Go example(which I'm not super familiar with Go just FYI), it appears that you may need to cast `data` to bytes.

See:
- https://godoc.org/cloud.google.com/go/pubsub#hdr-Publishing,
- https://github.com/GoogleCloudPlatform/google-cloud-go/blob/5af4269f950e91e917bab77f1138139023c868c2/pubsub/message.go#L32

The issue's for `google-cloud-go` are here https://github.com/GoogleCloudPlatform/google-cloud-go/issues.

@omaray, do you know who to contact about this being an issue in the console?

@daspecster I'd guess this is our problem too. It seems like we **ASSUME** that base64 values are properly padded but other libraries may strip padding to reduce payload size?


Resolving patch:
diff --git a/pubsub/google/cloud/pubsub/message.py b/pubsub/google/cloud/pubsub/message.py
--- a/pubsub/google/cloud/pubsub/message.py
+++ b/pubsub/google/cloud/pubsub/message.py
@@ -15,6 +15,7 @@
 """Define API Topics."""
 
 import base64
+import binascii
 
 from google.cloud._helpers import _rfc3339_to_datetime
 
@@ -85,7 +86,14 @@ def from_api_repr(cls, api_repr):
         :rtype: :class:`Message`
         :returns: The message created from the response.
         """
-        data = base64.b64decode(api_repr.get('data', b''))
+        raw_data = api_repr.get('data', b'')
+        try:
+            data = base64.b64decode(raw_data)
+        except (binascii.Error, TypeError):
+            to_pad = (- len(raw_data)) % 4
+            padded_data = raw_data + b'=' * to_pad
+            data = base64.b64decode(padded_data)
+
         instance = cls(
             data=data, message_id=api_repr['messageId'],
             attributes=api_repr.get('attributes'))


Test patch:
diff --git a/pubsub/unit_tests/test_message.py b/pubsub/unit_tests/test_message.py
--- a/pubsub/unit_tests/test_message.py
+++ b/pubsub/unit_tests/test_message.py
@@ -89,6 +89,19 @@ def test_from_api_repr_missing_data(self):
         self.assertEqual(message.attributes, {})
         self.assertIsNone(message.service_timestamp)
 
+    def test_from_api_repr_bad_b64_data(self):
+        DATA = b'wefwefw'
+        BAD_B64_DATA = b'd2Vmd2Vmdw='
+        MESSAGE_ID = '12345'
+        TIMESTAMP = '2016-03-18-19:38:22.001393427Z'
+        api_repr = {
+            'data': BAD_B64_DATA,
+            'messageId': MESSAGE_ID,
+            'publishTimestamp': TIMESTAMP,
+        }
+        message = self._getTargetClass().from_api_repr(api_repr)
+        self.assertEqual(message.data, DATA)
+
     def test_from_api_repr_no_attributes(self):
         from base64 import b64encode as b64
         DATA = b'DEADBEEF'