- Home /
UnityWebRequest and/or HttpWebRequest give 403 on Android with PUT
I am trying to post an image captured through my game to Facebook through their SDK, and as it requires a URI, I wrote a simple AWS Lambda function to take a byte array and upload it to an AWS bucket. The AWS function takes a call and then returns a URL to call PUT to with the image data... So the idea is, if the response is 200, use that URL to post to FB.
But - using Unity 5.2.x to 5.3.4, and Android 4.4 - 6.1 - it always gives me a 403 response, while working fine on iOS.
So, I have a method that takes a byte[] then does this:
     WWW w = new WWW("https://my-amazon-function-to-call");
     yield return w;
     if (!string.IsNullOrEmpty(w.error)) {
         Debug.LogError(w.error);
     }
     else 
     {
              var dict = Json.Deserialize(w.text) as Dictionary<string,object>;
              string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"]; // Private URL
              string resultUrl = (string)dict["resultUrl"]; // Public URL
             UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes);
             yield return aws.Send();
             if(aws.isError) 
             {
                 Debug.LogError("AWS ERROR: " + aws.error);
             }
             if(aws.responseCode == 200)
             {
                 FeedShare(new Uri(resultUrl), _cachedMessage);    // FB call
             }
     }
Pretty simple, right? On iOS, yes it is. But Android it continually gives me a 403 response on the PUT operation.
So, I've taken to wrapping this in an iOS-specific #ifdef and trying something more natively C-Sharp-ish for Android... Eg.:
     WWW w = new WWW("https://my-amazon-function-to-call"); // tried pure old Http too
     yield return w;
     if (!string.IsNullOrEmpty(w.error)) {
         Debug.LogError(w.error);
     }
     else {
         var dict = Json.Deserialize(w.text) as Dictionary<string,object>;
         string oneTimeUploadUrl = (string)dict["oneTimeUploadUrl"];
         string resultUrl = (string)dict["resultUrl"];
                     
     #if UNITY_IPHONE
         UnityWebRequest aws = UnityWebRequest.Put(oneTimeUploadUrl, bytes);
         yield return aws.Send();
         if(aws.isError) 
         {
             Debug.LogError("AWS ERROR: " + aws.error);
         }
         if(aws.responseCode == 200)
         {
             FeedShare(new Uri(resultUrl), _cachedMessage);    
         }
     #else             
     // Various Security Callback Tests
         //ServicePointManager.ServerCertificateValidationCallback = (p1, p2, p3, p4) => true;
         //ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
         ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
        
         HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(oneTimeUploadUrl);
         //wreq.AuthenticationLevel = System.Net.Security.AuthenticationLevel.None;
         //wreq.PreAuthenticate = false;
         wreq.Method = "PUT";
         wreq.ContentType = "image/png";
         wreq.ContentLength = bytes.Length;
         Stream newStream = wreq.GetRequestStream();
         newStream.Write(bytes, 0, bytes.Length);
         newStream.Close();
         
         HttpWebResponse response = (HttpWebResponse)wreq.GetResponse();
         if((int)response.StatusCode == 200)
         {
             FeedShare(new Uri(resultUrl), _cachedMessage);
         }
      #endif
     }
... but that also gives me a 403. I've tried a few different options, as you can see from the commented-out code, but no love. For the record, here are the SSL Policy functions that are used as the ServerCertificateValidationCallbacks:
 public bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
 {
     return true;
 }
 
 public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
     bool isOk = true;
     if (sslPolicyErrors != SslPolicyErrors.None) {
         for (int i=0; i<chain.ChainStatus.Length; i++) {
             if (chain.ChainStatus [i].Status != X509ChainStatusFlags.RevocationStatusUnknown) {
                 chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                 chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                 chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0);
                 chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                 bool chainIsValid = chain.Build ((X509Certificate2)certificate);
                 if (!chainIsValid) {
                     isOk = false;
                 }
             }
         }
     }
     print("MyRemoteCertificateValidationCallback: " + isOk);
     return isOk;
 }
This is the actual StackTrace:
 04-01 11:14:57.325: I/Unity(22979):  
 04-01 11:14:57.325: I/Unity(22979): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37)
 04-01 11:14:57.825: I/Unity(22979): WebException: The remote server returned an error: (403) Forbidden.
 04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.CheckFinalStatus (System.Net.WebAsyncResult result) [0x00000] in <filename unknown>:0 
 04-01 11:14:57.825: I/Unity(22979):   at System.Net.HttpWebRequest.SetResponseData (System.Net.WebConnectionData data) [0x00000] in <filename unknown>:0 
 04-01 11:14:57.825: I/Unity(22979):  
 04-01 11:14:57.825: I/Unity(22979): (Filename:  Line: -1)
At this point, I'm totally stuck... my only thought is to maybe write a native plugin... but would love to NOT have to do that...
Thoughts ???
Your answer
 
 
              koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                