12

I tried to upload a file to Google Cloud Storage using XML API. I have the right GoogleAccessId, expiry date and signature generated for each upload. The strange thing is that I can PUT file using Postman (application for Chrome), so I'm sure that the URL is ok. I just cannot PUT it using my Android Java program (it returns to me 403 error). The source code performing upload is here (it base on this one: https://cloud.google.com/storage/docs/access-control#Signing-Strings):

    URL url;
    HttpURLConnection connection;

    try {
        url = new URL("http://google-testbucket.storage.googleapis.com/testdata.txt?GoogleAccessId=1234567890123@developer.gserviceaccount.com&Expires=1331155464&Signature=BClz9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9sFpqXsQI8IQi1493mw%3D");
        connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("PUT");

        OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
        out.write("Test");
        out.close();

        Log.i("TAG", "PUT Response code: " + connection.getResponseCode());

    } catch (MalformedURLException e) {
        e.printStackTrace();
        Log.e("TAG", "MalformedURLException");
    } catch (ProtocolException e) {
        e.printStackTrace();
        Log.e("TAG", "ProtocolException");
    } catch (IOException e) {
        e.printStackTrace();
        Log.e("TAG", "IOException");
    }

Documentation for PUT Object: https://cloud.google.com/storage/docs/xml-api/put-object-upload

Can anybody look into this problem and give me hints what might went wrong with this one?

mysliwiec_tech
  • 649
  • 7
  • 21
  • Possible duplicate of http://stackoverflow.com/questions/17225638/forbiddenerror-when-attempting-to-write-file-to-gcs-from-gae-python-app Most likely your android app is not properly authenticated and in chrome you are (I assume you are logged in on your google account in chrome) – morpheus05 Dec 28 '15 at 09:42
  • I don't think this is a point, since for Postman it also works when I'm signed out from any of my accounts. There has to be different root cause, but i run out of ideas. – mysliwiec_tech Dec 28 '15 at 17:58
  • If you work with signed URLs the URL could be signed wrong, – morpheus05 Dec 28 '15 at 19:26
  • I'm almost sure it's not the case since the same parameters (including signed URL) works in Postman and in my Android application it doesn't. – mysliwiec_tech Dec 30 '15 at 10:44
  • 1
    i think this link have something in common with your question http://stackoverflow.com/questions/19890062/put-files-to-google-cloud-storage-gcs-via-signed-urls – youngdero Jan 05 '16 at 21:00
  • Thank you for your comment @youngdero. In simple words I can say that this was the resolution for my problems: http://stackoverflow.com/a/25118102/3572309. I had Content-Type header added automatically to my request and in this way it has to be present in signed string. – mysliwiec_tech Jan 05 '16 at 23:35

2 Answers2

11

I just figured out that HttpURLConnection adds Content-Type header with value application/x-www-form-urlencoded by itself. I've done it using HTTP sniffer on my android emulator.

This auto-added header caused signature mismatch. After I changed the code on the server-side to allow requests with Content-Type: application/x-www-form-urlencoded it generates the right signature and it works fine.

Thank you @morpheus05 for your commitment.

camelCaseCoder
  • 1,447
  • 19
  • 32
mysliwiec_tech
  • 649
  • 7
  • 21
1

Please set your Content-Type like this.

connection.setRequestProperty("Content-Type"," ");

Because HttpsUrlConnection automatically generate Content-Type as

"Content-Type: application/x-www-form-urlencoded"

this will cause a signature mismatch.

Zain Aftab
  • 703
  • 7
  • 21